#!/usr/bin/python

import os
import sys

class CmdMock(object):
    """
    Simple class mocking any shell command
    """

    stdoutFileName = 'stdout'
    exitStatusFileName = 'exit_status'
    argvFileName = 'args'

    def __init__(self, iowrappers, counters, dbPath):
        self.io = iowrappers
        self.counters = counters
        self.dbPath = dbPath

    def _makeCmdPath(self, cmd):
        """
        Make path to command data directory
        """
        return os.path.join(self.dbPath, cmd)

    def _makeCmdCntPath(self, cmd, cnt):
        """
        Make path to nth command invocation data directory
        """
        return os.path.join(self._makeCmdPath(cmd), cnt)

    def _makeCmdAssetPath(self, cmd, cnt, asset):
        """
        Make path to nth command invocation's data asset
        """
        return os.path.join(self._makeCmdCntPath(cmd, cnt), asset)

    def _processFileContents(self, cmd, cnt, asset, contentHandler):
        repeated = self._makeCmdAssetPath(cmd, '0', asset)
        sequential = self._makeCmdAssetPath(cmd, cnt, asset)
        rv = None

        if os.path.exists(repeated):
            # Prefer the '0' directory
            rv = contentHandler(repeated)

        elif os.path.exists(sequential):
            # Produce output from "cnt" directories sequentially for
            # each invocation
            rv = contentHandler(sequential)

        else:
            # No output at all
            pass

        return rv

    def _generateStdout(self, cmd, cnt):
        self._processFileContents(cmd, cnt,
                self.stdoutFileName,
                lambda fp: self.io.catFile(fp))

    def _getExitStatus(self, cmd, cnt):
        def _makeIntegerStatus(filePath):
            try:
                return int(self.io.slurpFile(filePath))
            except TypeError:
                pass
            except ValueError:
                pass

            return 0

        return self._processFileContents(cmd, cnt,
                self.exitStatusFileName, _makeIntegerStatus) or 0

    def _collectArgv(self, cmd, cnt):
        if (os.path.exists(self._makeCmdPath(cmd)) and
                os.path.exists(self._makeCmdCntPath(cmd, cnt))):
            argvPath = self._makeCmdAssetPath(cmd, cnt, self.argvFileName)
            for arg in sys.argv[1:]:
                self.io.echoFile(argvPath, arg + "\n")

    def programCall(self, cmd, so = None,
            es = 0,
            isRepeated = False):

        self.io.ensureDirectory(self._makeCmdPath(cmd))

        if isRepeated:
            cnt = '0'

        else:
            callPrograms = self.io.listDirectories(self._makeCmdPath(cmd))
            nEntries = len(callPrograms)
            if nEntries:
                if nEntries > 1:
                    callPrograms.sort(reverse = True,
                            cmp = lambda x,y: 0 if int(x) == int(y) else (
                                1 if int(x) > int(y) else -1))
                cnt = str(int(callPrograms[0]) + 1)
            else:
                cnt = '1'

        self.io.ensureDirectory(self._makeCmdCntPath(cmd, cnt))

        if so:
            self.io.copyFile(so, self._makeCmdAssetPath(cmd,
                cnt, self.stdoutFileName))

        if es:
            self.io.echoFile(self._makeCmdAssetPath(cmd,
                cnt, self.exitStatusFileName), str(es))

    def getArgv(self, cmd, cnt):
        argvPath = self._makeCmdAssetPath(cmd, cnt, self.argvFileName)
        rv = []
        if os.path.exists(argvPath):
            rv = self.io.slurpFileLines(argvPath)

        return rv


    def __call__(self, cmd):
        """
        Execute a command mock
        """
        self.counters.increment(cmd)
        cmdCnt = str(self.counters.get(cmd))
        self._collectArgv(cmd, cmdCnt)
        self._generateStdout(cmd, cmdCnt)
        return self._getExitStatus(cmd, cmdCnt)
