from __future__ import unicode_literals
import zipfile
import os.path
import sys
import xbmc, xbmcvfs, xbmcgui
from . import dropbox
from . import utils as utils
from .dropbox.files import WriteMode,CommitInfo,UploadSessionCursor
from . authorizers import DropboxAuthorizer

class Vfs:
    root_path = None

    def __init__(self,rootString):
        self.set_root(rootString)
        
    def set_root(self,rootString):
        old_root = self.root_path
        self.root_path = rootString
        
        #fix slashes
        self.root_path = self.root_path.replace("\\","/")
        
        #check if trailing slash is included
        if(self.root_path[-1:] != "/"):
            self.root_path = self.root_path + "/"

        #return the old root
        return old_root
        
    def listdir(self,directory):
        return {}

    def mkdir(self,directory):
        return True

    def put(self,source,dest):
        return True

    def rmdir(self,directory):
        return True

    def rmfile(self,aFile):
        return True

    def exists(self,aFile):
        return True
    
    def rename(self,aFile,newName):
        return True
    
    def cleanup(self):
        return True
        
class XBMCFileSystem(Vfs):

    def listdir(self,directory):
        return xbmcvfs.listdir(directory)

    def mkdir(self,directory):
        return xbmcvfs.mkdir(xbmc.translatePath(directory))

    def put(self,source,dest):
        return xbmcvfs.copy(xbmc.translatePath(source),xbmc.translatePath(dest))
        
    def rmdir(self,directory):
        return xbmcvfs.rmdir(directory,True)

    def rmfile(self,aFile):
        return xbmcvfs.delete(aFile)

    def rename(self,aFile,newName):
        return xbmcvfs.rename(aFile, newName)

    def exists(self,aFile):
        return xbmcvfs.exists(aFile)

class ZipFileSystem(Vfs):
    zip = None
    
    def __init__(self,rootString,mode):
        self.root_path = ""
        self.zip = zipfile.ZipFile(rootString,mode=mode,compression=zipfile.ZIP_DEFLATED,allowZip64=True)
        
    def listdir(self,directory):
        return [[],[]]
    
    def mkdir(self,directory):
        #self.zip.write(directory[len(self.root_path):])
        return False
    
    def put(self,source,dest):
        
        aFile = xbmcvfs.File(xbmc.translatePath(source),'r')
        
        self.zip.writestr(dest,aFile.readBytes())
        
        return True
    
    def rmdir(self,directory):
        return False
    
    def exists(self,aFile):
        return False
    
    def cleanup(self):
        self.zip.close()
        
    def extract(self,aFile,path):
        #extract zip file to path
        self.zip.extract(aFile,path)
    
    def listFiles(self):
        return self.zip.infolist()

class DropboxFileSystem(Vfs):
    MAX_CHUNK = 50 * 1000 * 1000 #dropbox uses 150, reduced to 50 for small mem systems
    client = None
    APP_KEY = ''
    APP_SECRET = ''
    
    def __init__(self,rootString):
        self.set_root(rootString)

        authorizer = DropboxAuthorizer()

        if(authorizer.isAuthorized()):
            self.client = authorizer.getClient()
        else:
            #tell the user to go back and run the authorizer
            xbmcgui.Dialog().ok(utils.getString(30010),utils.getString(30105))
            sys.exit()

    def listdir(self,directory):
        directory = self._fix_slashes(directory)
        
        if(self.client != None and self.exists(directory)):
            files = []
            dirs = []
            metadata = self.client.files_list_folder(directory)

            for aFile in metadata.entries:
                if(isinstance(aFile,dropbox.files.FolderMetadata)):
                    dirs.append(aFile.name)
                else:
                    files.append(aFile.name)

            return [dirs,files]
        else:
            return [[],[]]
            

    def mkdir(self,directory):
        directory = self._fix_slashes(directory)
        if(self.client != None):
            #sort of odd but always return true, folder create is implicit with file upload
            return True
        else:
            return False

    def rmdir(self,directory):
        directory = self._fix_slashes(directory)
        if(self.client != None and self.exists(directory)):
            #dropbox is stupid and will refuse to do this sometimes, need to delete recursively
            dirs,files = self.listdir(directory)
            
            for aDir in dirs:
                self.rmdir(aDir)

            #finally remove the root directory
            self.client.files_delete(directory)
            
            return True
        else:
            return False

    def rmfile(self,aFile):
        aFile = self._fix_slashes(aFile)
        
        if(self.client != None and self.exists(aFile)):
            self.client.files_delete(aFile)
            return True
        else:
            return False

    def exists(self,aFile):
        aFile = self._fix_slashes(aFile)
        
        if(self.client != None):
            #can't list root metadata
            if(aFile == ''):
                return True
            
            try:
                meta_data = self.client.files_get_metadata(aFile)
                #if we make it here the file does exist
                return True
            except:
                return False
        else:
            return False

    def put(self,source,dest,retry=True):
        dest = self._fix_slashes(dest)
        
        if(self.client != None):
            #open the file and get its size
            f = open(source,'rb')
            f_size = os.path.getsize(source)
            
            try:
                if(f_size < self.MAX_CHUNK):
                    #use the regular upload
                    response = self.client.files_upload(f.read(),dest,mode=WriteMode('overwrite'))
                else:
                    #start the upload session
                    upload_session = self.client.files_upload_session_start(f.read(self.MAX_CHUNK))
                    upload_cursor = UploadSessionCursor(upload_session.session_id,f.tell())
                    
                    while(f.tell() < f_size):
                        #check if we should finish the upload
                        if((f_size - f.tell()) <= self.MAX_CHUNK):
                            #upload and close
                            self.client.files_upload_session_finish(f.read(self.MAX_CHUNK),upload_cursor,CommitInfo(dest,mode=WriteMode('overwrite')))
                        else:
                            #upload a part and store the offset
                            self.client.files_upload_session_append_v2(f.read(self.MAX_CHUNK),upload_cursor)
                            upload_cursor.offset = f.tell()
                    
                #if no errors we're good!   
                return True
            except Exception as anError:
                utils.log(str(anError))
                
                #if we have an exception retry
                if(retry):
                    return self.put(source,dest,False)
                else:
                    #tried once already, just quit
                    return False
        else:
            return False

    def get_file(self,source,dest):
        if(self.client != None):
            #write the file locally
            f = self.client.files_download_to_file(dest,source)
            return True
        else:
            return False

    def _fix_slashes(self,filename):
        result = filename.replace('\\','/')

        #root needs to be a blank string
        if(result == '/'):
            result = ""

        #if dir ends in slash, remove it
        if(result[-1:] == "/"):
            result = result[:-1]

        return result
