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

# Copyright 2010 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.

import os
from os import path
import re
import sys
import pwd
import time
import traceback

from datavars import DataVarsDesktop, DataVars, __version__,__app__

from calculate.lib.cl_template import Template, ProgressTemplate,TemplatesError
from calculate.lib.utils.files import runOsCommand, isMount
from calculate.lib.utils.common import getpathenv,appendProgramToEnvFile, \
                                       removeProgramToEnvFile
from calculate.core.server.func import safetyWrapper

from calculate.lib.cl_lang import setLocalTranslate,getLazyLocalTranslate
setLocalTranslate('cl_desktop3',sys.modules[__name__])
__ = getLazyLocalTranslate(_)

class DesktopError(Exception):
    """Desktop Error"""

class Desktop:
    """
    Desktop logic object

    Has fundamental method createHome for configure user profile
    """
    # username
    userName = ""
    verbose = False
    
    def __init__(self):
        self.homeDir = ""
        self.clTempl = None
        self.clVars = None

    def createUserDir(self, uid, gid, userDir, mode=0700):
        """
        Create user directory with need uid and gid
        """
        if not path.exists(userDir):
            os.makedirs(userDir)
            if mode:
                os.chmod(userDir,mode)
            os.chown(userDir,uid,gid)
            return True
        else:
            raise DesktopError(_("Path %s exists") %userDir)

    def displayTemplatesApplied(self,dirsFiles):
        """
        Display templates are applied (--verbose)
        """
        self.printSUCCESS(_("The following files were changed")+":")
        for nameF in dirsFiles[1]:
            nameFile = nameF
            if nameFile[:1] != "/":
                nameFile = "/" + nameFile
            self.printSUCCESS(" "*5 + nameFile)

    def applyTemplatesFromUser(self):
        """Apply templates for user"""
        if self.clTempl:
            self.closeClTemplate()

        templates_locate = self.clVars.Get('cl_templates_locate')
        self.clVars.Set("cl_template_path",
            map(lambda x:x[1],
            filter(lambda x:x[0] in templates_locate,
            zip(self.clVars.Get('cl_template_location'),
                self.clVars.Get('cl_template_path')))),
            True)

        self.clTempl = ProgressTemplate(self.setProgress,self.clVars,
                                        cltObj=False,
                                        printERROR=self.printERROR,
                                        printWARNING=self.printWARNING,
                                        printSUCCESS=self.printSUCCESS)
        dirsFiles = self.clTempl.applyTemplates()
        if self.clTempl.getError():
            self.printERROR(self.clTempl.getError().strip())
            return False
        else:
            if self.verbose:
                self.displayTemplatesApplied(dirsFiles)
            return dirsFiles

    def initVars(self,datavars=None):
        """Primary variables initialization"""
        if not datavars:
            self.clVars = DataVarsDesktop()
            self.clVars.importDesktop()
            self.clVars.flIniFile()
        else:
            self.clVars = datavars

    def closeClTemplate(self):
        if self.clTempl:
            if self.clTempl.cltObj:
                self.clTempl.cltObj.closeFiles()
            self.clTempl.closeFiles()
            self.clTempl = None

    def umountUserRes(self, error):
        """
        Umount user directory
        """
        self.closeClTemplate()
        if error and self.homeDir:
            umountPaths = self.getMountUserPaths(self.homeDir)
            ret = True
            for umountPath in umountPaths:
                if not self.umountSleepPath(umountPath):
                    ret = False
                    break
            return ret

    @safetyWrapper(native_errors=(TemplatesError,DesktopError),
                   man_int=__("Configuration manually interrupted"),
                   post_action=umountUserRes)
    def createHome(self, datavars=None):
        """
        Creating user profile and userdir
        """
        self.initVars(datavars)
        self.verbose = self.clVars.Get('cl_verbose_set') == 'on'
        #uid = os.getuid()
        #try:
        #    realUserName = pwd.getpwuid(uid).pw_name
        #except:
        #    realUserName = ""
        userName = self.clVars.Get("ur_login")
        uidGid = False
        if self.clVars.isModuleInstalled("client"):
            # domain host
            domain = self.clVars.Get("client.cl_remote_host")
            # authorized in domain or local
            hostAuth = self.clVars.Get("client.os_remote_auth")
        else:
            domain = ""
            hostAuth = ""
        uid = self.clVars.Get('ur_uid')
        gid = self.clVars.Get('ur_gid')
        if not uid or not gid:
            raise DesktopError(_("Failed to determine the user UID"))
        uid,gid = int(uid),int(gid)

        self.homeDir = self.clVars.Get('ur_home_path')
        rootPath = self.clVars.Get('cl_root_path')
        # real path to home dir
        self.homeDir = path.join(rootPath, self.homeDir[1:])
        if not path.exists(self.homeDir):
            self.startTask(_("Creating the home directory for %s")%self.homeDir)
            self.createUserDir(uid,gid,self.homeDir)
            self.endTask()
        # action - "user profile configuration"
        self.clVars.Set("cl_action", "desktop", True)
        # apply user profiles
        self.startTask(_("Setting up the user profile"),progress=True)
        dirsAndFiles = self.applyTemplatesFromUser()
        self.endTask()
        if not dirsAndFiles:
            raise DesktopError(_("Failed to apply user profile templates"))
        self.printSUCCESS(_("User account %s is configured")%userName + " ...")
        return True

    def getMountUserPaths(self, homeDir=False):
        """
        Found user resources
        """
        if not homeDir:
            userName = self.clVars.Get("ur_login")
            homeDir = self.clVars.Get("ur_home_path")
        if not homeDir:
            raise DesktopError(_("Failed to determine home directory"))
        dirStart, dirEnd = path.split(homeDir)
        mountProfileDir = path.join(dirStart, ".%s" %dirEnd)
        mountRemoteProfileDir = path.join(dirStart, ".%s.remote" %dirEnd)
        return filter(lambda x: x.startswith(homeDir) or\
                      x.startswith(mountProfileDir) or\
                      x.startswith(mountRemoteProfileDir),
                      map(lambda x: x.split(" ")[1],\
                          open("/proc/mounts").readlines()))

    def execProg(self, cmdStrProg, inStr=False, envProg={}):
        """
        Exec external program
        """
        env_path = {"PATH":getpathenv()}
        env = {}
        env.update(os.environ.items() + env_path.items() + envProg.items())
        retCode,programOut = runOsCommand(cmdStrProg,in_str=inStr,env_dict=env)
        if not retCode:
            return programOut
        return False

    def umountSleepPath(self, rpath):
        """
        Unmount path, sleep by failed and repeat
        """
        # check for mount
        if isMount(rpath):
            for waittime in [0,0.5,1,2]:
                time.sleep(waittime)
                if not self.execProg("umount %s"%rpath) is False \
                    or not isMount(rpath):
                    if not isMount(rpath):
                        return True
            self.execProg("fuser -km %s"%rpath)
            for waittime in [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]:
                time.sleep(waittime)
                if not self.execProg("umount %s"%rpath) is False \
                    or not isMount(rpath):
                    if not isMount(rpath):
                        return True
            else:
                if isMount(rpath):
                    self.printERROR(_("Failed to unmount directory %s")%rpath)
                    return False
        return True

