#  This file is part of Headphones.
#
#  Headphones 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 3 of the License, or
#  (at your option) any later version.
#
#  Headphones 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 Headphones.  If not, see <http://www.gnu.org/licenses/>.

#####################################
## Stolen from Sick-Beard's db.py  ##
#####################################

from __future__ import with_statement

import os
import sqlite3
import threading
import time

import headphones

from headphones import logger

db_lock = threading.Lock()

def dbFilename(filename="headphones.db"):

    return os.path.join(headphones.DATA_DIR, filename)
    
def getCacheSize():
    #this will protect against typecasting problems produced by empty string and None settings
    if not headphones.CACHE_SIZEMB:
        #sqlite will work with this (very slowly)
        return 0
    return int(headphones.CACHE_SIZEMB)

class DBConnection:

    def __init__(self, filename="headphones.db"):
    
        self.filename = filename
        self.connection = sqlite3.connect(dbFilename(filename), timeout=20)
        #don't wait for the disk to finish writing
        self.connection.execute("PRAGMA synchronous = OFF")
        #journal disabled since we never do rollbacks
        self.connection.execute("PRAGMA journal_mode = OFF")        
        #64mb of cache memory,probably need to make it user configurable
        self.connection.execute("PRAGMA cache_size=-%s" % (getCacheSize()*1024))
        self.connection.row_factory = sqlite3.Row
        
    def action(self, query, args=None):
    
        with db_lock:

            if query == None:
                return
                
            sqlResult = None
            attempt = 0
            
            while attempt < 5:
                try:
                    if args == None:
                        #logger.debug(self.filename+": "+query)
                        sqlResult = self.connection.execute(query)
                    else:
                        #logger.debug(self.filename+": "+query+" with args "+str(args))
                        sqlResult = self.connection.execute(query, args)
                    self.connection.commit()
                    break
                except sqlite3.OperationalError, e:
                    if "unable to open database file" in e.message or "database is locked" in e.message:
                        logger.warn('Database Error: %s' % e)
                        attempt += 1
                        time.sleep(1)
                    else:
                        logger.error('Database error: %s' % e)
                        raise
                except sqlite3.DatabaseError, e:
                    logger.error('Fatal Error executing %s :: %s' % (query, e))
                    raise
            
            return sqlResult
    
    def select(self, query, args=None):
    
        sqlResults = self.action(query, args).fetchall()
        
        if sqlResults == None:
            return []
            
        return sqlResults
                    
    def upsert(self, tableName, valueDict, keyDict):
    
        changesBefore = self.connection.total_changes
        
        genParams = lambda myDict : [x + " = ?" for x in myDict.keys()]
        
        query = "UPDATE "+tableName+" SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join(genParams(keyDict))
        
        self.action(query, valueDict.values() + keyDict.values())
        
        if self.connection.total_changes == changesBefore:
            query = "INSERT INTO "+tableName+" (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \
                        " VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")"
            self.action(query, valueDict.values() + keyDict.values())
