#-*- coding: utf-8 -*-

# Copyright 2008-2012 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

from calculate.lib.utils.files import (readLinesFile,readFile,pathJoin,
                                       process)
from itertools import *
from os import path
import os
import re
import hashlib

class PkgContents:
    """
    Object for work with CONTENTS file
    """
    reCfg = re.compile(r"/\._cfg\d{4}_")
    reObj = re.compile(
        r"^(?:"
        r"(?:(?P<sym>sym)\s+(?P<symname>.*)\s+->"
        r"\s+(?P<symtarget>.*)\s+(?P<symtime>\S+)\s*)|"
        r"(?:(?P<dirfif>dir|fif)\s+(?P<dirname>.*)\s*)|"
        r"(?:(?P<obj>obj)\s+(?P<filename>.*)\s+"
        r"(?P<md5>\S+)\s+(?P<filetime>\S+)\s*))$")
    def __init__(self,pkg,prefix="/"):
        self.prefix = prefix
        self.contentFile = path.join(prefix,'var/db/pkg/%s/CONTENTS'%pkg)
        self.readContents()

    def _identifyLine(self,line):
        res = self.reObj.search(line.strip())
        if res:
            res = res.groupdict()
            if res.get('dirfif',''):
                return (res['dirname'],{'type':res['dirfif']})
            elif res.get('obj',''):
                return (res['filename'],{'type':"obj",
                                         'md5':res['md5'],
                                         'mtime':res['filetime']})
            elif res.get('sym',''):
                return (res['symname'],{'type':'sym',
                                        'target':res['symtarget'],
                                        'mtime':res['symtime']})
    
    def readContents(self):
        """
        Re-read contents
        """
        self.content = dict(filter(None,map(self._identifyLine,
                       readLinesFile(self.contentFile))))

    def writeContents(self):
        """
        Write current content
        """
        f = open(self.contentFile,'w')
        for filename in sorted(self.content.keys(),
                        key=lambda x:(1 if x.startswith('/etc') else 0,x)):
            value = self.content[filename]
            formats = {'obj':"obj {filename} {md5} {mtime}\n",
                       'sym':"sym {filename} -> {target} {mtime}\n",
                       'dir':"dir {filename}\n",
                       'fif':"fif {filename}\n"}
            f.write(formats[value['type']].format(filename=filename,**value))
        f.close()

    def addDir(self,filename):
        filename = self.reCfg.sub("/",filename)
        if filename != '/':
            if not filename in self.content or \
                self.content[filename]['type'] != 'dir':
                self.addDir(path.dirname(filename))
                self.content[filename] = {'type':'dir'}

    def addFile(self,filename):
        newfilename = pathJoin(self.prefix,filename)
        filename = self.reCfg.sub("/",filename)
        self.content[filename] = {'type':'obj',
                  'md5':hashlib.md5(readFile(newfilename)).hexdigest(),
                  'mtime':str(int(os.stat(newfilename).st_mtime))}

    def addLink(self,filename):
        newfilename = pathJoin(self.prefix,filename)
        filename = self.reCfg.sub("/",filename)
        self.content[filename] = {'type':'sym',
                  'target':os.readlink(newfilename),
                  'mtime':str(int(os.lstat(newfilename).st_mtime))}

    def removeObject(self,filename):
        filename = self.reCfg.sub("/",filename)
        if filename in self.content:
            self.content.pop(filename)

    def addObject(self,filename):
        """
        Add object to content
        """
        newfilename = pathJoin(self.prefix,filename)
        if filename != '/':
            self.addDir(path.dirname(filename))
            if path.islink(newfilename):
                self.addLink(filename)
            elif path.isdir(newfilename):
                self.addDir(filename)
            elif path.isfile(newfilename):
                self.addFile(filename)

def getCfgFiles(protected_dirs=['/etc']):
    """
    Get protected cfg files
    """
    reCfg = re.compile(r"/\._cfg\d{4}_",re.S)
    findParams = ["find"]+protected_dirs+\
        ["-name","._cfg????_*","!","-name",".*~","!","-iname",".*.bak",
        "-printf",r"%T@ %p\n"]
    mapCfg = {}
    for filetime,sep,filename in map(lambda x:x.partition(' '),
         filter(None,process(*findParams))):
        origFilename = reCfg.sub(r'/',filename)
        if not origFilename in mapCfg:
            mapCfg[origFilename] = []
        mapCfg[origFilename].append((int(filetime.split('.')[0]),filename))
    return mapCfg
