#
#       Copyright (C) 2014
#       Sean Poyser (seanpoyser@gmail.com)
#
#  This Program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2, or (at your option)
#  any later version.
#
#  This Program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with XBMC; see the file COPYING.  If not, write to
#  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#  http://www.gnu.org/copyleft/gpl.html
#

import urllib
import os
import threading
import re

import copy

import sfile
import dixie


DSF     = dixie.isDSF()
PATH    = os.path.join(dixie.GetChannelFolder(), 'channels')
CATFILE = os.path.join(dixie.PROFILE, 'catfile')
GUARD   = '~OTTV-GUARD'


def tidy(text):
    if isinstance(text, bool):
        return '1' if text else '0'

    if (not isinstance(text, unicode)) and (not isinstance(text, str)):
        text = str(text)
    text = text.replace('\n', '')
    text = text.replace('\r', '')
    text = text.replace('\\', '/')
    return text


def Filename(text):
    text = text.replace('*', '_star')
    text = text.replace('+', '_plus')
    text = text.replace(' ', '_')

    text = re.sub('[:\\/?\<>|"]', '', text)
    text = text.strip()
    try:    text = text.encode('ascii', 'ignore')
    except: text = text.decode('utf-8').encode('ascii', 'ignore')

    return text

    

class Channel(object):
    def __init__(self, id, title='', logo='', streamUrl='', visible=1, weight=-1, categories='', userDef=0, desc='', isClone=0, isFave=0, offset=0):
        if isinstance(id, list):
            self.setFromList(id)
        else:
            self.set(id, title, logo, streamUrl, visible, weight, categories, userDef, desc, isClone, isFave, offset)

        # dixie.log(self)


    def set(self, id, title, logo, streamUrl, visible, weight, categories, userDef, desc, isClone, isFave, offset):
        if logo:
            logo = tidy(logo)

        self.id         = tidy(id)
        self.title      = tidy(title)
        self.categories = tidy(categories)
        self.logo       = logo
        self.streamUrl  = tidy(streamUrl)

        self.visible = int(tidy(visible))
        self.weight  = int(tidy(weight))
        self.userDef = int(tidy(userDef))
        self.isClone = int(tidy(isClone))

        try:     self.isFave = int(tidy(isFave))
        except:  self.isFave = 0

        try:     self.offset = int(tidy(offset))
        except:  self.offset = 0

        self.desc = tidy(desc)

        if DSF:
            self.title = urllib.unquote_plus(self.title)

        FAVOURITE = dixie.GetSetting('FAVOURITE') + '|'
        if FAVOURITE in self.categories:
            self.categories = self.categories.replace(FAVOURITE, dixie.GetFaveCategory() + '|')


    def isProtected(self):
        return dixie.ADULT in self.categories


    def setFromList(self, list):
        userDef = False
        isClone = False
        isFave  = False
        offset  = 0
        desc    = ''

        if len(list) > 7:
            userDef = list[7]

        if len(list) > 8:
            desc = list[8]

        if len(list) > 9:
            isClone = list[9]
        
        if len(list) > 10:
            isFave = list[10]

        if len(list) > 11:
            offset = list[11]


        self.set(list[0], list[1], list[2], list[3], list[4], list[5], list[6], userDef, desc, isClone, isFave, offset)


    def safeWriteToFile(self, file, text):
        if text:
            try:    file.write(text.encode('utf8'))
            except: file.write(text)

        file.write('\n')


    def writeToFile(self, filename=None):
        if not filename:
            filename = os.path.join(PATH, Filename(self.id)) 

        cloneID = -1
        localID = self.id

        if self.isClone:
            filename, cloneID = self.cloneFilename(filename)
            localID += cloneID

        try:    f = sfile.file(filename, 'w')
        except: return False

        self.safeWriteToFile(f, localID)
        self.safeWriteToFile(f, self.title)
        self.safeWriteToFile(f, self.logo)
        self.safeWriteToFile(f, self.streamUrl)

        if self.visible:
            f.write('1\n')
        else:
            f.write('0\n')

        f.write(str(self.weight) + '\n')

        self.safeWriteToFile(f, self.categories)

        if self.userDef:
            f.write('1\n')
        else:
            f.write('0\n')

        self.safeWriteToFile(f, self.desc)

        if self.isClone:
            f.write('1\n')
        else:
            f.write('0\n')

        if self.isFave:
            f.write('1\n')
        else:
            f.write('0\n')

        f.write(str(self.offset) + '\n')

        f.close()
        return True


    def cloneFilename(self, filename):
        if '_clone_' in filename:
            return filename, ''

        index    = 1
        root     = filename
        filename = root + '_clone_%d' % index

        while sfile.exists(filename):
            index += 1
            filename = root + '_clone_%d' % index

        return filename, '_clone_%d' % index


    def clone(self):
        return copy.deepcopy(self)
      

    def compare(self, channel):
        if self.visible != channel.visible:
            return False

        if self.weight != channel.weight:
            return False

        if self.title != channel.title:
            return False

        if self.logo != channel.logo:
            return False

        if self.categories != channel.categories:
           return False

        if self.isFave != channel.isFave:
           return False

        if self.offset != channel.offset:
           return False

        return True


    def __eq__(self, other):
        if not hasattr(self, 'id'):
            return False

        if not hasattr(other, 'id'):
            return False

        return self.id == other.id


    def __repr__(self):
        try:
            return 'Channel(id=%s, title=%s, categories=%s, logo=%s, streamUrl=%s, weight=%s, visible=%s userDef=%s desc=%s isClone=%s isFave=%s, offset=%s)' \
               % (self.id, self.title, self.categories, self.logo, self.streamUrl, str(self.weight), str(self.visible), str(self.userDef), self.desc, str(self.isClone), str(self.isFave), str(self.offset))
        except:
            return 'Can\'t display channel'


    def getWeight(self):
        return self.weight


    def updateCategories(self, categoryList):
        # add bookend |'s
        mycategories = '|%s|' % self.categories

        updated = False

        for category in categoryList:
            newName = category[0]
            oldName = category[1]

            if newName == oldName:
                continue

            newName = '|%s%s|' % (newName, GUARD)
            oldName = '|%s|' % oldName

            if oldName not in mycategories:
                continue

            if not updated:
                # dixie.log('%s : Updating categories' % self.title)
                # dixie.log('Current categories = %s' % self.categories)
                updated = True
            
            # dixie.log('Updating %s to %s' % (category[1], category[0]))

            mycategories = mycategories.replace(oldName, newName) 

        if not updated:
            return

        # remove GUARD and bookends |'s
        self.categories = mycategories.replace(GUARD, '')[1:-1]
        # dixie.log('New categories: %s' % self.categories)
        # dixie.log('================================================================')

        self.writeToFile()
        


#############################################################


def GetChannelFromFile(filename):
    try:    return Channel(sfile.readlines(filename))
    except: return None


def _ConfirmUpdate(folder):
    # dixie.log('==== Confirm Update ====')
    src = os.path.join(folder, 'tempchannels')
    dst = os.path.join(folder, 'channels')

    # dixie.log('==== src/dst ====')
    # dixie.log(src)
    # dixie.log(dst)
    CheckForNewChannels(src)

    newFiles = []

    current, dirs, files = sfile.walk(src)
    for file in files:
        filename = os.path.join(dst, file)
        if not sfile.exists(filename):
            newFiles.append(file)

    if len(newFiles) == 0:
        return

    channels = []
    titles   = ['[I]All Channels[/I]']

    for file in newFiles:
        channel = GetChannelFromFile(os.path.join(src, file))
        if channel:
            channels.append(channel)
            titles.append(channel.title)

    import xbmcgui

    selections = xbmcgui.Dialog().multiselect('New channels detected - please choose those you wish to add', titles)

    if not selections:
        selections = []

    addAll = 0 in selections

    for idx, channel in enumerate(channels):
        visible = addAll
        if idx+1 in selections: # +1 to account for 'All Channels' item
            visible = True

        channel.visible = visible
        channel.writeToFile(os.path.join(dst, newFiles[idx]))

    sfile.rmtree(src)


def ConfirmUpdate(folder):
    try:
        return _ConfirmUpdate(folder)
    except Exception, e:
        dixie.DialogOK(str(e))
        # dixie.log('Error %s in Channel.ConfirmUpdate' % str(e))


def CheckForNewChannels(src):
    # dixie.log('==== Check For New Channels ====')
    if not os.path.exists(src):
        sfile.makedirs(src)

    logoFolder = dixie.GetLogoFolder() # Get the logo folder setting.
    if dixie.GetSetting('logo.type') == '1':
        logoPath = logoFolder
    else:
        logoPath = 'special://profile/addon_data/script.tvguidedixie/extras/logos/' + logoFolder + '/'

    newChannelList = dixie.loadChannelList() # Read channels.json and return a list. File is downloaded in DB.zip.
    oldChannelList = dixie.getAllChannels()  # Read files in existing channels folder and return a list

    newChannels   = []
    oldChannels   = []
    oldCategories = []

    for oldChannel in oldChannelList:
        oldChannelTitle = oldChannel[2].title
        oldChannels.append(oldChannelTitle)

        oldChannelCats = oldChannel[2].categories.split('|')

        for category in oldChannelCats:
            if category not in oldCategories:
                oldCategories.append(category)

    # # dixie.log('==== oldChannels ====')
    # # dixie.log(oldChannels)
    # # dixie.log('==== oldCategories ====')
    # # dixie.log(oldCategories)

    for newIdx, newChannel in enumerate(newChannelList):
        newIdx     = newIdx+1
        id         = newChannel[0]
        title      = newChannel[1]
        categories = newChannel[2].split('|')
        logo       = logoPath + title + '.png'

        if title not in oldChannels:
            newCategories  = checkCategories(oldCategories, categories)
            channelToAdd   = Channel(id, title, weight=newIdx, logo=logo, categories=newCategories)
            newChannelItem = [newIdx, id, channelToAdd]
            newChannels.append(newChannelItem)
    # # dixie.log('==== newChannels ====')
    # # dixie.log(newChannels)

    for item in newChannels:
        ch   = item[2]
        chID = item[1]
        ch.writeToFile(os.path.join(src, dixie.CleanFilename(chID)))


def checkCategories(oldCategories, newCategories):
    for cat in newCategories:
        if cat in oldCategories:
            return cat

    return cat[0]


def GetChannelFromFile(id):
    path = os.path.join(PATH, id)

    if not sfile.exists(path):
        return None

    cfg = sfile.readlines(path)

    return Channel(cfg)


class ChannelLoader(threading.Thread):
    def __init__(self, id):
        super(ChannelLoader, self).__init__()
        self.id      = id
        self.channel = None

    def run(self):
        try:    self.channel = GetChannelFromFile(self.id)
        except: self.channel = None


def GetChannelsFromFile(channelIDs):
    loaders = []
    for id in channelIDs:
        channel = ChannelLoader(id)
        channel.start()
        loaders.append(channel)

    [loader.join() for loader in loaders]

    return [loader.channel for loader in loaders]


def GetCategoriesFromFile():
    # dixie.log('========== GetCategoriesFromFile ==========')
    if os.path.exists(CATFILE):
        return sfile.readlines(CATFILE)

    message = 'Updating Category Names'
    dixie.notify(message)
    # dixie.log(message)

    datapath    = dixie.PROFILE
    destination = os.path.join(datapath, 'channels')
    channels    = dixie.getAllChannels() # Read files in existing channels folder and return a list

    if len(channels) == 0:
        return []

    createCatFile(channels)

    for channel in channels:
        chan = channel[2]

        categories = chan.categories

        cats = categories.split('|')
        # dixie.log('++++++ TEST CATS at split ++++++')
        # dixie.log(cats)

        catlist = []
        for cat in cats:
            newcat = simplifyCat(cat)

            catlist.append(newcat)

            if len(catlist) == 1:
                newcategories = catlist[0]
            else:
                newcategories = '|'.join(catlist)

        chan.categories = newcategories
        chan.writeToFile(os.path.join(destination, dixie.CleanFilename(chan.id)))

    return sfile.readlines(CATFILE)


def UpdateCatFile(categoryList):
    categories = ''
    # dixie.log('Update Category File with %s' % categoryList)

    for cat in categoryList:
        categories += cat[0]
        categories += ',%d' % cat[2]
        categories += '\n'

    #remove final \n
    categories = categories[:-1]

    sfile.write(CATFILE, categories)


def createCatFile(channels):
    oldcats  = []

    for channel in channels:
        categories = channel[2].categories.split('|')

        for category in categories:
            if category not in oldcats:
                oldcats.append(category)

    oldcats.sort()
    # # dixie.log('========== oldcats ==========')
    # # dixie.log(oldcats)

    FAVOURITE = dixie.GetSetting('FAVOURITE')

    categories = dixie.GetFaveCategory()
    categories += ',1'
    categories += '\n'

    for oldcat in oldcats:
        if oldcat == FAVOURITE:
            continue

        newcat = simplifyCat(oldcat)

        categories += newcat
        categories += ',1'
        categories += '\n'

    #remove final \n
    categories = categories[:-1]

    # # dixie.log(categories)
    sfile.write(CATFILE, categories)


def simplifyCat(cat):
    # dixie.log('++++++ TEST CATS at simplifyCat ++++++')
    # dixie.log(cat.split('. ', 1)[-1])

    return cat.split('. ', 1)[-1]
