#!/usr/bin/python
# -*- coding: utf-8 -*-

import re
import db
import fb2_meta
import os
import sys
import shutil

mapping = None

def GetTagsMapping(db):
  global mapping
  c = db.cursor()
  c.execute("select tag_mask,tag_result from metadata.tags_mapping")
  mapping = c.fetchall()
  result = []
  for item in mapping:
    result.append((re.compile(item[0]),item[1].encode('utf-8')))
  mapping = result
  
def Matches(tag):
  global mapping
  for item in mapping:
    if item[0].match(tag):
      return item[1]
  return tag

def NewTags(tags):
  global mapping
  if not mapping:
    GetTagsMapping(db.database)
  new_tags = set()
  for item in tags:
    new_tags.add(Matches(item))
  return list(new_tags)

class MetaData:

  def GetPath(self):
    self.sort_title = db.SortName(self.title).replace(' ','_');
    self.sort_author = db.SortAuthorName(self.author).replace(' ','_');
    self.path = ("%s/%s/%s/%s/%s (%d)" % (self.sort_author[0],self.sort_author[0:2],self.sort_author[0:4],self.sort_author[0:32],self.sort_title[0:64],self.book_id))
    self.dataname = (self.title.decode('utf-8')[0:64]+' -- '+self.author.decode('utf-8')[0:32]).replace('/','')

  def __init__(self,meta_dict,size):

    self.size = size
    try:
      tags=NewTags(meta_dict['tags'])
    except:
      tags=['other']  
    if 'trash' in tags:
      self.state="trash"
      return
    self.tags=tags
    try:
      tag=tags[0]
    except:
      tag='Жанр неизвестен'  

    try:
      self.author=meta_dict['authors'][0].encode('utf-8')
      self.authors = meta_dict['authors']
      author_ids = set()
      try:
        for author in self.authors:
          print author.encode('utf-8')
          author_ids.add(db.GetOrCreateAuthor(author.encode('utf-8')))
      except:
        pass
    except:
      self.author='Неизвестный Автор (%s)' % (tag)
      self.authors = []
      author_ids = []

    try:
      try:
        self.langs=meta_dict['languages']    
      except:
        self.langs=['ru']  
      lang_ids = set()
      for lang in meta_dict['languages']:
        lang_ids.add(db.GetOrCreateLang(lang.encode('utf-8')))
    except:
      pass
      
    
    try:
      self.publisher = meta_dict['publisher'].encode('utf-8')
      pub_id=db.GetOrCreatePublisher(self.publisher)
    except:
      pub_id=None
 
    try:
      title = meta_dict['book_title'].encode('utf-8')
    except:
      title='Название неизвестно'
    self.title=title

    try:
      pub_date=meta_dict['pubdate']
    except:
      pub_date=None
    self.pub_date=pub_date

    try:
      isbn=meta_dict['isbn'].encode('utf-8')
    except:
      isbn=None
    self.isbn=isbn

    try:
      self.series = meta_dict['series'].encode('utf-8')
      ser_id=db.GetOrCreateSeries(meta_dict['series'])
    except:
      ser_id=None
    try:
      self.series_idx = meta_dict['series_index']
      ser_num=meta_dict['series_index'].split(',')[0]
    except:
      ser_num=None

    tag_ids = set()
    try:
      for tag in tags:
        tag_ids.add(db.GetOrCreateTag(tag))
    except:
      pass
      
    try:
      self.cover=meta_dict['cover']
      self.has_cover=1
    except:
      self.has_cover=0        

    try:
      self.comments=meta_dict['comments']
      if len(self.comments)>20000:
        self.comments=self.comments[:20000]
      self.comments=self.comments.encode('utf-8')
    except:
      self.comments=''  

    book_id = db.CreateBook(title,pub_date,ser_num,isbn)
    self.book_id = book_id
   
    db.LinkBookToAuthors(book_id,author_ids);
    db.LinkBookToLangs(book_id,lang_ids);
    if pub_id:
      db.LinkBookToPublishers(book_id,pub_id);
    if ser_id:
      db.LinkBookToSeries(book_id,ser_id);
    db.LinkBookToTags(book_id,tag_ids);
    if self.comments:
      db.StoreComment(book_id,self.comments)
  
    self.GetPath()
    db.SetPath(self.book_id,self.path,self.dataname,self.size,self.has_cover);
    self.state="done"

def ProcessFile(filename):

  size = os.path.getsize(filename)
  stream = open(filename)
  meta = fb2_meta.get_metadata(stream)
  stream.close()

  try:
    book = MetaData(meta.__dict__,size) 

    if book.state=="done":

      new_file_path = db.file_root + book.path + '/' + book.dataname + '.fb2'
      cover_path = db.file_root + book.path + '/cover.jpg'
      new_dir_path = db.file_root + book.path 

      os.makedirs(new_dir_path,0755)
      shutil.move(filename,new_file_path)

      if book.has_cover:
        cover_path = new_dir_path + '/cover.jpg'
        print "Book has cover, try to store to "+cover_path
        coverfile = open(cover_path,'w')
        coverfile.write(book.cover.decode('base64'))
        coverfile.close()

      db.Commit()
      print "Moved to "+new_dir_path

    elif book.state=="trash":
   
      print "Tags blacklisted, trashing"
      os.remove(filename) 
    
    else: 
    
      shutil.move(filename,db.failed_files+os.path.basename(filename))
      print "Moved to failed_files"
      db.Rollback()  
    
  except:

    shutil.move(filename,db.failed_files+os.path.basename(filename))
    print "Moved to failed_files"
    db.Rollback()  

def ProcessDir(dirname):
  for file in os.listdir(dirname):
    if file.endswith(".fb2"):
      print "Processing "+file
      ProcessFile(os.path.join(dirname,file))

def DelBook(id):
  path = os.path.join(db.file_root,db.PathByID(id))
  if path:
    for file in os.listdir(path):
      os.remove(os.path.join(path,file))
    db.DelBook(id)
    os.rmdir(path)
    db.Commit()  

def CompressBook(id):
  path=db.PathByID(id)
  if path:
    datafile = os.path.join(db.file_root,path,db.DataByID(id,'FB2'))
    datapath = datafile.replace("\"","\\\"")
    datapath=datapath.replace("`","\`")
    datapath=datapath.replace("$","\$")
    zipfile = datapath + '.zip'
    command = "zip --move --junk-paths \"%s\" \"%s\"" % (zipfile,datapath)
    command = command.encode('utf-8')
    print command
    if os.system(command)==0:
      db.ChangeBookFormat(id,'FB2','FB2.ZIP')
      db.Commit()

def UnCompressBook(id):
  path=db.PathByID(id)
  if path:
    datafile = os.path.join(db.file_root,path,db.DataByID(id,'FB2.ZIP'))
    datapath = datafile.replace("\"","\\\"")
    datapath=datapath.replace("`","\`")
    datapath=datapath.replace("$","\$")
    command = "unzip  \"%s\" -d \"%s\"" % (datapath,os.path.join(db.file_root,path))
    command = command.encode('utf-8')
    'print command'
    if os.system(command)==0:
      os.remove(datafile)
      db.ChangeBookFormat(id,'FB2.ZIP','FB2')
      db.Commit()

def CompressAll(limit=100):
  ids = db.ListByFormat('FB2',limit)
  for id in ids:
    try:
      CompressBook(id[0])
    except:
      pass  

def CheckFiles(delete = 0):
  ids = db.ListByFormat('FB2',1000000)
  cnt = 0
  for id in ids:
    cnt = cnt + 1;
    sys.stdout.write("\r%s"%(cnt))
    datafile = os.path.join(db.file_root,db.PathByID(id[0]),db.DataByID(id[0],'FB2'))
    if not os.path.exists(datafile):
      print "\r File %s not found" % (datafile)
      if delete==1:
        db.DelBook(id)
        db.Commit()
  ids = db.ListByFormat('FB2.ZIP',1000000)
  cnt = 0
  for id in ids:
    cnt = cnt + 1;
    sys.stdout.write("\r%s"%(cnt))
    datafile = os.path.join(db.file_root,db.PathByID(id[0]),db.DataByID(id[0],'FB2.ZIP'))
    if not os.path.exists(datafile):
      print "\r File %s not found" % (datafile)
      if delete==1:
        db.DelBook(id)
        db.Commit()

def RemoveDups(limit = 100):
  if limit<2:
    return
  id_to_del=set([]) 
  recs = db.ListDups(limit);
  for rec in recs:
    ids = db.ListByTitleAndAuthor(rec[0],rec[1],rec[2])
    for id in ids:
      id_to_del.add(id)
  for id in id_to_del:
    print "\r Deleting %s..." % (id)
    DelBook(id)

def main():
  print "Processing...\r"
  ProcessDir(db.tmp_files)
  CompressAll(2000)

if __name__ == "__main__":
    import sys
    reload(sys)
    sys.setdefaultencoding('utf-8')
    main()
               