#!/usr/bin/env python
from __future__ import print_function
import os, sys
import argparse
import functools
import shlex

from quickfind.Searcher import Searcher, Ranker, CursesPrinter
import quickfind.Console as Console
from quickfind.source.DirectorySource import DirectorySource, ranker, dirFormatter
from quickfind.source.CtagsSource import CtagsSource, CtagsRanker, CtagsFormatter
from quickfind.source.Util import rec_dir_up,StringRanker, simpleFormatter

class Runner(object):
    def __init__(self, args):
        self.rows, self.columns = Console.getDims()
        self.args = args

    def find(self):
        raise NotImplementedError()

class DirRun(Runner):

    def find(self):

        git_ignore = self.args.g
        if self.args.fileType == 'all':
            ds = DirectorySource(ignore_directories=False, 
                    ignore_files=False, git_ignore=git_ignore)
        elif self.args.fileType == 'files':
            ds = DirectorySource(ignore_files=False, git_ignore=git_ignore)
        else:
            ds = DirectorySource(ignore_directories=False, git_ignore=git_ignore)

        items = ds.fetch()

        sr = ranker(self.args.w, self.args.p)
        s = Searcher(sr, CursesPrinter(dirFormatter))
        try:
            found = s.run(items)
        except KeyboardInterrupt:
            sys.exit(0)

        if found is not None:
            found = os.path.join(found.dir, found.name)
        return found 

class StdinRun(Runner):
    def fetch(self):
        results = []
        for i,line in enumerate(sys.stdin):
            results.append((i, line.strip()))

        # reopen stdin
        f = open("/dev/tty")
        os.dup2(f.fileno(), 0)
        return results

    def find(self):

        items = self.fetch()
        sr = StringRanker.new(self.args.w, get_part=lambda _, x: x[1])
        s = Searcher(sr, CursesPrinter(lambda x,q,d: simpleFormatter(x[1], q,d)))
        try:
            found = s.run(items)
        except KeyboardInterrupt:
            sys.exit(0)

        if found is not None:
            return found[1]

class CtagsRun(Runner):

    def find(self):
        ctagsFile = self.find_ctag_file()
        if ctagsFile is None:
            print("Could not find ctags file")
            sys.exit(1)

        items = CtagsSource(ctagsFile).fetch()
        s = Searcher(CtagsRanker, CursesPrinter(CtagsFormatter(self.columns)))
        try:
            found = s.run(items)
        except KeyboardInterrupt:
            sys.exit(0)
        if found is not None:
            return found.file

    def find_ctag_file(self):
        directory = os.getcwd()
        for dir in rec_dir_up(directory):
            path = os.path.join(dir, "tags")
            if os.path.isfile(path):
                return path

        return None

def build_arg_parser():
    parser = argparse.ArgumentParser(description='Quickfind on various archives.')
    parser.add_argument('-c', 
            action="store_true", help='Ctags quickfind')
    parser.add_argument('-s', 
            help='Writes the file to provided file instead of executing it.')
    parser.add_argument('-g', 
            action="store_false", help='Do not filter out files in .gitignore')
    parser.add_argument('-w', 
            action="store_true", help='White space separates multiple queries')
    parser.add_argument('-p', 
            action="store_true", help='Match also on path')
    parser.add_argument('-e', default="%s {}" % os.environ['EDITOR'],
            help="Command to execute on selection.  Default is '$EDITOR {}'.")
    parser.add_argument('-', dest='stdin',
            action="store_true", help="Reads from stdin.")
    parser.add_argument('-D', dest='delim', default=None,
            help="Delimiter for {}-style parsing.")
            

    group = parser.add_mutually_exclusive_group()
    group.add_argument('-d', action="store_const", dest="fileType", const="dirs",
                        help='Directories only.')
    group.add_argument('-a', action="store_const", dest="fileType", const="all",
                        help='Both files and directories.')
    parser.set_defaults(fileType="files")

    return parser.parse_args()

def main():
    args = build_arg_parser()
    if args.c:
        runner = CtagsRun(args)
    elif args.stdin:
        runner = StdinRun(args)
    else:
        runner = DirRun(args)

    item = runner.find()
    if item is not None:
        if args.s is not None:
            with file(args.s, 'w') as f:
                 f.write(item)
        else:
            formatted = args.e.format(item, *item.split(args.delim))
            pieces = shlex.split(formatted)
            os.execvp(pieces[0], pieces)      

if __name__ == '__main__':
    main()
