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

import os
import re
import sys
import glob
import json
import linecache
import shutil

import click


# Filenames follow the format (prefix, number, suffix, extension)
BASE_NAME = '{}{:03d}{}{}'
FILE_RE = re.compile(r'(.*)(\d{3})(.*)(\.\w+)')

EULER_DATA = os.path.join(os.path.dirname(__file__), 'data')

class Problem(object):
    """Represents a Project Euler problem of a given problem number"""
    def __init__(self, problem_number):
        self.num = problem_number

    def filename(self, prefix='', suffix='', extension='.py'):
        """Returns filename padded with leading zeros"""
        return BASE_NAME.format(prefix, self.num, suffix, extension)

    @property
    def glob(self):
        """Returns a sorted glob of files belonging to a given problem"""
        file_glob = glob.glob(BASE_NAME.format('*', self.num, '*', '.*'))

        # Sort globbed files by tuple (filename, extension)
        return sorted(file_glob, key=lambda f: os.path.splitext(f))

    @property
    def file(self):
        """Returns a ProblemFile instance of the first matching file"""
        return ProblemFile(self.glob[0]) if self.glob else None

    @property
    def resources(self):
        """Returns a list of resources related to the problem (or None)"""
        with open(os.path.join(EULER_DATA, 'resources.json')) as data_file:
            data = json.load(data_file)

        problem_num = str(self.num)

        if problem_num in data:
            files = data[problem_num]

            # Ensure a list of files is returned
            return files if isinstance(files, list) else [files]
        else:
            return None

    def copy_resources(self):
        """Copies the relevant resources to a resources subdirectory"""
        if not os.path.isdir('resources'):
            os.mkdir('resources')

        resource_dir = os.path.join(os.getcwd(), 'resources', '')
        copied_resources = []

        for resource in self.resources:
            src = os.path.join(EULER_DATA, 'resources', resource)
            if os.path.isfile(src):
                shutil.copy(src, resource_dir)
                copied_resources.append(resource)

        if copied_resources:
            copied = ', '.join(copied_resources)
            path = os.path.relpath(resource_dir, os.pardir)
            msg = "Copied {} to {}.".format(copied, path)

            click.secho(msg, fg='green')

    @property
    def solution(self):
        """Returns the answer to a given problem"""
        num = self.num

        solution_file = os.path.join(EULER_DATA, 'solutions.txt')
        solution_line = linecache.getline(solution_file, num)

        try:
            answer = solution_line.split('. ')[1].strip()
        except IndexError:
            answer = None

        if answer:
            return answer
        else:
            msg = 'Answer for problem %i not found in solutions.txt.' % num
            click.secho(msg, fg='red')
            click.echo('If you have an answer, consider submitting a pull '
                       'request to EulerPy on GitHub.')
            sys.exit(1)

    @property
    def text(self):
        """Parses problems.txt and returns problem text"""
        def _problem_iter(problem_num):
            problem_file = os.path.join(EULER_DATA, 'problems.txt')

            with open(problem_file) as f:
                is_problem = False
                last_line = ''

                for line in f:
                    if line.strip() == 'Problem %i' % problem_num:
                        is_problem = True

                    if is_problem:
                        if line == last_line == '\n':
                            break
                        else:
                            yield line[:-1]
                            last_line = line

        problem_lines = [line for line in _problem_iter(self.num)]

        if problem_lines:
            # First three lines are the problem number, the divider line,
            # and a newline, so don't include them in the returned string.
            # Also, strip the final newline.
            return '\n'.join(problem_lines[3:-1])
        else:
            msg = 'Problem %i not found in problems.txt.' % self.num
            click.secho(msg, fg='red')
            click.echo('If this problem exists on Project Euler, consider '
                       'submitting a pull request to EulerPy on GitHub.')
            sys.exit(1)


class ProblemFile(object):
    """Represents a file that belongs to a given Project Euler problem"""
    def __init__(self, filename):
        self.filename = filename

    def __str__(self):
        return self.filename

    @property
    def _filename_parts(self):
        """Returns (prefix, number, suffix, extension)"""
        return FILE_RE.search(self.filename).groups()

    @property
    def prefix(self):
        return self._filename_parts[0]

    @property
    def str_num(self):
        return self._filename_parts[1]

    @property
    def suffix(self):
        return self._filename_parts[2]

    @property
    def extension(self):
        return self._filename_parts[3]

    @property
    def num(self):
        return int(self.str_num)

    def change_suffix(self, suffix):
        if suffix == self.suffix:
            return False

        new_name = self.prefix + self.str_num + suffix + self.extension
        os.rename(self.filename, new_name)

        msg = 'Renamed "{}" to "{}".'.format(self.filename, new_name)
        click.secho(msg, fg='yellow')
        self.filename = new_name

        return True