import click
import os
import sys

from morenines.index import Index
from morenines.ignores import Ignores
from morenines.util import get_files, get_hash, get_new_and_missing
from morenines.output import success, warning, error, print_filelists

_path_type = {
    'file':click.Path(file_okay=True, dir_okay=False, resolve_path=True),
    'existing file':click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
    'new file':click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True),
    'existing dir':click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
}

_common_params = {
    'index': click.argument('index_file', required=False, type=_path_type['file']),
    'ignored': click.option('-i', '--ignored/--no-ignored', 'show_ignored', default=False),
}


def common_params(*param_names):
    def real_decorator(func):
        for param_name in param_names:
            func = _common_params[param_name](func)
        return func

    return real_decorator


class MNContext(object):
    def __init__(self, index, ignores):
        self.index = index
        self.ignores = ignores


def get_index(index_path):
    if not index_path:
        index_path = find_file('.mnindex')

    if index_path:
        index = Index.read(index_path)
    else:
        index = None

    return index


def get_ignores(index):
    if index.ignores_file:
        ignores_path = index.ignores_file
    else:
        ignores_path = os.path.join(index.root_path, '.mnignore')

    if os.path.isfile(ignores_path):
        ignores = Ignores.read(ignores_path, index.root_path)
    else:
        ignores = Ignores()

    return ignores


def get_context(index_path, index_required=True):
    index = get_index(index_path)

    if not index and index_required:
        error("Cannot find index file '.mnindex' in this or any parent dir")
        # XXX XXX TODO write util.abort() or something, to exit centrally
        import sys
        sys.exit(1)

    if index:
        ignores = get_ignores(index)
    else:
        ignores = Ignores()

    return MNContext(index, ignores)


@click.group()
def main():
    pass


@main.command()
@click.option('--ignores-file', 'ignores_path', type=_path_type['existing file'])
@click.option('-o', '--output', 'output_path', default=os.path.join(os.getcwd(), '.mnindex'), type=_path_type['new file'])
@click.argument('root_path', required=True, default=os.getcwd(), type=_path_type['existing dir'])
def create(ignores_path, root_path, output_path):
    if os.path.exists(output_path):
        error("Index file {} already exists".format(output_path))
        error("(To update an existing index file, use the 'update' command)")
        sys.exit(1)

    if os.path.basename(output_path) == '-':
        output_path = '-'

    index = Index(root_path, ignores_path)

    ignores = Ignores.read(ignores_path, root_path)

    files, ignored = get_files(index.root_path, ignores)

    index.add(files)

    with click.open_file(output_path, mode='w') as stream:
        index.write(stream)

    success('Wrote index file {}'.format(output_path))


@main.command()
@common_params('index')
@click.option('--remove-missing/--no-remove-missing', default=False)
@click.option('--new-root', 'new_root', type=_path_type['existing dir'])
def update(index_file, remove_missing, new_root):
    index = Index.read(index_file)

    if new_root:
        index.root_path = new_root

    ignores = Ignores.read(index.ignores_file, index.root_path)

    new_files, missing_files, ignored_files = get_new_and_missing(index, ignores)

    index.add(new_files)

    if remove_missing is True:
        index.remove(missing_files)

    index.write(sys.stdout)


@main.command()
@common_params('index', 'ignored')
def status(index_file, show_ignored):
    index = Index.read(index_file)

    ignores = Ignores.read(index.ignores_file, index.root_path)

    new_files, missing_files, ignored_files = get_new_and_missing(index, ignores, show_ignored)

    print_filelists(new_files, None, missing_files, ignored_files)


@main.command()
@common_params('index', 'ignored')
def verify(index_file, show_ignored):
    index = Index.read(index_file)

    ignores = Ignores.read(index.ignores_file, index.root_path)

    new_files, missing_files, ignored_files = get_new_and_missing(index, ignores, show_ignored)

    changed_files = []

    for path, old_hash in index.files.iteritems():
        if path in missing_files:
            continue

        current_hash = get_hash(os.path.join(index.root_path, path))

        if current_hash != old_hash:
            changed_files.append(path)

    print_filelists(new_files, changed_files, missing_files, ignored_files)


if __name__ == '__main__':
    main()

@@ -3353,34 +3353,37 @@
 t):%0A    
-ind
+cont
 ex
+t
  = 
-Index.read
+get_context
 (index_f
@@ -3409,24 +3409,32 @@
 ot:%0A        
+context.
 index.root_p
@@ -3453,73 +3453,8 @@
 ot%0A%0A
-    ignores = Ignores.read(index.ignores_file, index.root_path)%0A%0A
     
@@ -3511,31 +3511,47 @@
 and_missing(
-index, 
+context.index, context.
 ignores)%0A%0A  
@@ -3544,32 +3544,40 @@
 t.ignores)%0A%0A    
+context.
 index.add(new_fi
@@ -3617,24 +3617,32 @@
 ue:%0A        
+context.
 index.remove
@@ -3658,24 +3658,32 @@
 files)%0A%0A    
+context.
 index.write(
@@ -3793,102 +3793,40 @@
     
-ind
+cont
 ex
+t
  = 
-Index.read(index_file)%0A%0A    ignores = Ignores.read(index.ignores_file, index.root_path
+get_context(index_file
 )%0A%0A 
@@ -3882,39 +3882,55 @@
 new_and_missing(
-index, 
+context.index, context.
 ignores, show_ig
@@ -4103,102 +4103,40 @@
     
-ind
+cont
 ex
+t
  = 
-Index.read(index_file)%0A%0A    ignores = Ignores.read(index.ignores_file, index.root_path
+get_context(index_file
 )%0A%0A 
@@ -4200,23 +4200,39 @@
 missing(
-index, 
+context.index, context.
 ignores,
@@ -4297,16 +4297,24 @@
 hash in 
+context.
 index.fi
@@ -4423,32 +4423,40 @@
 sh(os.path.join(
+context.
 index.root_path,

Make the commands call get_context()