# -*- coding: utf-8 -*-
# Copyright: Damien Elmes <oldanki@ichi2.net>
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html

"""\
Exporting support
==============================
"""
__docformat__ = 'restructuredtext'

import itertools, time, re, os, HTMLParser
from operator import itemgetter
from oldanki import DeckStorage
from oldanki.cards import Card
from oldanki.sync import SyncClient, SyncServer, copyLocalMedia
from oldanki.lang import _
from oldanki.utils import findTag, parseTags, stripHTML, ids2str
from oldanki.tags import tagIds
from oldanki.db import *

class Exporter(object):
    def __init__(self, deck):
        self.deck = deck
        self.limitTags = []
        self.limitCardIds = []

    def exportInto(self, path):
        self._escapeCount = 0
        file = open(path, "wb")
        self.doExport(file)
        file.close()

    def escapeText(self, text, removeFields=False):
        "Escape newlines and tabs, and strip Anki HTML."
        from BeautifulSoup import BeautifulSoup as BS
        text = text.replace("\n", "<br>")
        text = text.replace("\t", " " * 8)
        if removeFields:
            # beautifulsoup is slow
            self._escapeCount += 1
            if self._escapeCount % 100 == 0:
                self.deck.updateProgress()
            try:
                s = BS(text)
                all = s('span', {'class': re.compile("fm.*")})
                for e in all:
                    e.replaceWith("".join([unicode(x) for x in e.contents]))
                text = unicode(s)
            except HTMLParser.HTMLParseError:
                pass
        return text

    def cardIds(self):
        "Return all cards, limited by tags or provided ids."
        if self.limitCardIds:
            return self.limitCardIds
        if not self.limitTags:
            cards = self.deck.s.column0("select id from cards")
        else:
            d = tagIds(self.deck.s, self.limitTags, create=False)
            cards = self.deck.s.column0(
                "select cardId from cardTags where tagid in %s" %
                ids2str(d.values()))
        self.count = len(cards)
        return cards

class AnkiExporter(Exporter):

    key = _("Anki Deck (*.oldanki)")
    ext = ".oldanki"

    def __init__(self, deck):
        Exporter.__init__(self, deck)
        self.includeSchedulingInfo = False
        self.includeMedia = True

    def exportInto(self, path):
        n = 3
        if not self.includeSchedulingInfo:
            n += 1
        self.deck.startProgress(n)
        self.deck.updateProgress(_("Exporting..."))
        try:
            os.unlink(path)
        except (IOError, OSError):
            pass
        self.newDeck = DeckStorage.Deck(path)
        client = SyncClient(self.deck)
        server = SyncServer(self.newDeck)
        client.setServer(server)
        client.localTime = self.deck.modified
        client.remoteTime = 0
        self.deck.s.flush()
        # set up a custom change list and sync
        lsum = self.localSummary()
        rsum = server.summary(0)
        self.deck.updateProgress()
        payload = client.genPayload((lsum, rsum))
        self.deck.updateProgress()
        res = server.applyPayload(payload)
        if not self.includeSchedulingInfo:
            self.deck.updateProgress()
            self.newDeck.s.statement("""
delete from reviewHistory""")
            self.newDeck.s.statement("""
update cards set
interval = 0,
lastInterval = 0,
due = created,
lastDue = 0,
factor = 2.5,
firstAnswered = 0,
reps = 0,
successive = 0,
averageTime = 0,
reviewTime = 0,
youngEase0 = 0,
youngEase1 = 0,
youngEase2 = 0,
youngEase3 = 0,
youngEase4 = 0,
matureEase0 = 0,
matureEase1 = 0,
matureEase2 = 0,
matureEase3 = 0,
matureEase4 = 0,
yesCount = 0,
noCount = 0,
spaceUntil = 0,
type = 2,
relativeDelay = 2,
combinedDue = created,
modified = :now
""", now=time.time())
            self.newDeck.s.statement("""
delete from stats""")
        # media
        if self.includeMedia:
            server.deck.mediaPrefix = ""
            copyLocalMedia(client.deck, server.deck)
        # need to save manually
        self.newDeck.rebuildCounts()
        self.newDeck.updateAllPriorities()
        self.exportedCards = self.newDeck.cardCount
        self.newDeck.utcOffset = -1
        self.newDeck.s.commit()
        self.newDeck.close()
        self.deck.finishProgress()

    def localSummary(self):
        cardIds = self.cardIds()
        cStrIds = ids2str(cardIds)
        cards = self.deck.s.all("""
select id, modified from cards
where id in %s""" % cStrIds)
        facts = self.deck.s.all("""
select facts.id, facts.modified from cards, facts where
facts.id = cards.factId and
cards.id in %s""" % cStrIds)
        models = self.deck.s.all("""
select models.id, models.modified from models, facts where
facts.modelId = models.id and
facts.id in %s""" % ids2str([f[0] for f in facts]))
        media = self.deck.s.all("""
select id, created from media""")
        return {
            # cards
            "cards": cards,
            "delcards": [],
            # facts
            "facts": facts,
            "delfacts": [],
            # models
            "models": models,
            "delmodels": [],
            # media
            "media": media,
            "delmedia": [],
            }

class TextCardExporter(Exporter):

    key = _("Text files (*.txt)")
    ext = ".txt"

    def __init__(self, deck):
        Exporter.__init__(self, deck)
        self.includeTags = False

    def doExport(self, file):
        ids = self.cardIds()
        strids = ids2str(ids)
        self.deck.startProgress((len(ids) + 1) / 50)
        self.deck.updateProgress(_("Exporting..."))
        cards = self.deck.s.all("""
select cards.question, cards.answer, cards.id from cards
where cards.id in %s
order by cards.created""" % strids)
        self.deck.updateProgress()
        if self.includeTags:
            self.cardTags = dict(self.deck.s.all("""
select cards.id, facts.tags from cards, facts
where cards.factId = facts.id
and cards.id in %s
order by cards.created""" % strids))
        out = u"\n".join(["%s\t%s%s" % (
            self.escapeText(c[0], removeFields=True),
            self.escapeText(c[1], removeFields=True),
            self.tags(c[2]))
                          for c in cards])
        if out:
            out += "\n"
        file.write(out.encode("utf-8"))
        self.deck.finishProgress()

    def tags(self, id):
        if self.includeTags:
            return "\t" + ", ".join(parseTags(self.cardTags[id]))
        return ""

class TextFactExporter(Exporter):

    key = _("Text files (*.txt)")
    ext = ".txt"

    def __init__(self, deck):
        Exporter.__init__(self, deck)
        self.includeTags = False

    def doExport(self, file):
        cardIds = self.cardIds()
        self.deck.startProgress()
        self.deck.updateProgress(_("Exporting..."))
        facts = self.deck.s.all("""
select factId, value, facts.created from facts, fields
where
facts.id in
(select distinct factId from cards
where cards.id in %s)
and facts.id = fields.factId
order by factId, ordinal""" % ids2str(cardIds))
        txt = ""
        self.deck.updateProgress()
        if self.includeTags:
            self.factTags = dict(self.deck.s.all(
                "select id, tags from facts where id in %s" %
                ids2str([fact[0] for fact in facts])))
        groups = itertools.groupby(facts, itemgetter(0))
        groups = [[x for x in y[1]] for y in groups]
        groups = [(group[0][2],
                   "\t".join([self.escapeText(x[1]) for x in group]) +
                   self.tags(group[0][0]))
                  for group in groups]
        self.deck.updateProgress()
        groups.sort(key=itemgetter(0))
        out = [ret[1] for ret in groups]
        self.count = len(out)
        out = "\n".join(out)
        file.write(out.encode("utf-8"))
        self.deck.finishProgress()

    def tags(self, id):
        if self.includeTags:
            return "\t" + self.factTags[id]
        return ""

# Export modules
##########################################################################

def exporters():
    return (
        (_("Anki Deck (*.oldanki)"), AnkiExporter),
        (_("Cards in tab-separated text file (*.txt)"), TextCardExporter),
        (_("Facts in tab-separated text file (*.txt)"), TextFactExporter))
