import sys
import os
import optparse
import traceback

from dosage import events, scraper
from dosage.output import out
from dosage.util import getWindowSize
from dosage.version import VERSION

def setupOptions():
    usage = 'usage: %prog [options] comicModule [comicModule ...]'
    parser = optparse.OptionParser(usage=usage)
    parser.add_option('-v', '--verbose', action='count', dest='verbose', default=0, help='provides verbose output, use multiple times for more verbosity')
    parser.add_option('-q', '--quiet', action='count', dest='quiet', default=0, help='suppress all output')
    parser.add_option('-c', '--catch-up', action='count', dest='catchup', default=None, help='traverse and retrieve all available comics up until the strip that already exists locally, use twice to retrieve until all strips exist locally')
    parser.add_option('-b', '--base-path', action='store', dest='basepath', default='Comics', help='set the path to create invidivual comic directories in, default is Comics', metavar='PATH')
    parser.add_option('--base-url', action='store', dest='baseurl', default=None, help='the base URL of your comics directory (for RSS, HTML, etc.); this should correspond to --base-path', metavar='PATH')
    parser.add_option('-l', '--list', action='store_const', const=1, dest='list', help='list available comic modules')
    parser.add_option('--single-list', action='store_const', const=2, dest='list', help='list available comic modules in a single list')
    parser.add_option('-V', '--version', action='store_true', dest='version', help='display the version number')
    parser.add_option('-m', '--module-help', action='store_true', dest='modhelp', help='display help for comic modules')
    parser.add_option('-t', '--timestamps', action='store_true', dest='timestamps', default=False, help='print timestamps for all output at any info level')
    parser.add_option('-o', '--output', action='store', dest='output', choices=events.getHandlers(), help='output formatting for downloaded comics')
    try:
        getWindowSize()
    except NotImplementedError:
        progress = False
    else:
        progress = True

    if progress:
        parser.add_option('-p', '--progress', action='store_true', dest='progress', default=False, help='display progress bar while downloading comics')
    return parser

class Dosage(object):
    def setOutputInfo(self):
        out.level = 0
        out.level += self.settings['verbose']
        out.level -= self.settings['quiet']
        out.timestamps = self.settings['timestamps']

    def saveComic(self, comic):
        basepath = self.settings['basepath']
        progress = self.settings.get('progress', False)
        fn, saved = comic.save(basepath, progress)
        return saved

    def saveComics(self, comics):
        saved = False
        for comic in comics:
            saved = self.saveComic(comic) or saved
        return saved

    def safeOp(self, fp, *args, **kwargs):
        try:
            fp(*args, **kwargs)
        except:
            type, value, tb = sys.exc_info()
            out.write('Traceback (most recent call last):', 1)
            out.writelines(traceback.format_stack(), 1)
            out.writelines(traceback.format_tb(tb)[1:], 1)
            out.writelines(traceback.format_exception_only(type, value), 0)

    def getCurrent(self):
        out.write('Retrieving the current strip...', 0)
        self.saveComics(self.module.getCurrentComics())

    def getIndex(self, index):
        out.write('Retrieving index "%s"....' % (index,), 0)
        try:
            self.module.setStrip(index)
            self.saveComics(self.module.getNextComics())
        except NotImplementedError:
            out.write('No indexed retrieval support.')

    def catchup(self):
        out.write('Catching up...', 0)
        for comics in self.module:
            if not self.saveComics(comics) and self.settings['catchup'] < 2:
                break

    def catchupIndex(self, index):
        out.write('Catching up from index "%s"...' % (index,), 0)
        self.module.setStrip(index)
        for comics in self.module:
            if not self.saveComics(comics) and self.settings['catchup'] < 2:
                break

    def getScrapers(self):
        return scraper.items()

    def getExistingComics(self):
        for scraper in self.getScrapers():
            dirname = scraper.name.replace('/', os.sep)
            if os.path.isdir(os.path.join(self.settings['basepath'], dirname)):
                yield scraper

    def doList(self, columnList):
        out.write('Available comic scrapers:', 0)
        scrapers = self.getScrapers()
        if columnList:
            self.doColumnList(scrapers)
        else:
            self.doSingleList(scrapers)
        out.write('%d supported comics.' % len(scrapers), 0)

    def doSingleList(self, scrapers):
        print '\n'.join(scraper.name for scraper in scrapers)

    def doColumnList(self, scrapers):
        try:
            screenWidth = getWindowSize()
        except NotImplementedError:
            screenWidth = 80

        if len(scrapers) == 0:
            return

        names = [scraper.name for scraper in scrapers]
        maxlen = max([len(name) for name in names])
        namesPerLine = int(screenWidth / (maxlen + 1))

        while names:
            print ''.join([name.ljust(maxlen) for name in names[:namesPerLine]])
            del names[:namesPerLine]

    def doCatchup(self):
        for comic in self.useComics():
            if self.indices:
                self.safeOp(self.catchupIndex, self.indices[0])
            else:
                self.safeOp(self.catchup)

    def doCurrent(self):
        for comic in self.useComics():
            if self.indices:
                for index in self.indices:
                    self.safeOp(self.getIndex, index)
            else:
                self.safeOp(self.getCurrent)

    def doHelp(self):
        for scraper in self.useComics():
            out.write('Help for %s:' % (scraper.name,), 0, context='')
            for line in scraper.getHelp().splitlines():
                out.write(line, -1)

    def setupComic(self, scraper):
        self.module = scraper()
        out.context = scraper.name
        return self.module

    def useComics(self):
        for comic in self.comics:
            c = comic.split(':', 2)
            if len(c) > 1:
                self.indices = c[1].split(',')
            else:
                self.indices = None

            moduleName = c[0]
            if moduleName == '@':
                for s in self.getExistingComics():
                    yield self.setupComic(s)
            elif moduleName == '@@':
                for s in self.getScrapers():
                    yield self.setupComic(s)
            else:
                yield self.setupComic(scraper.get(moduleName))

    def displayVersion(self):
        print '''Dosage v.%s
Copyright (C) 2004-2008 Jonathan Jacobs and Tristan Seligmann
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.''' % VERSION

    def displayShortVersion(self):
        print VERSION

    def run(self, settings, comics):
        self.settings = settings
        self.setOutputInfo()
        self.comics = comics

        om = self.settings['output']
        events.installHandler(om, self.settings['basepath'], self.settings['baseurl'])
        events.handler.start()

        if self.settings['list']:
            self.doList(self.settings['list'] == 1)
        elif self.settings['version']:
            if out.level < 0:
                self.displayShortVersion()
            else:
                self.displayVersion()
        elif len(comics) <= 0:
            out.write('Warning: No comics specified, bailing out!', 0)
        elif self.settings['modhelp']:
            self.doHelp()
        elif self.settings['catchup']:
            self.doCatchup()
        else:
            self.doCurrent()

        events.handler.end()

def main():
    parser = setupOptions()
    options, args = parser.parse_args()
    d = Dosage()
    d.run(options.__dict__, args)

if __name__ == '__main__':
    main()
