#    Copyright 2014 Mirantis, Inc.
#
#    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.
from __future__ import print_function

import json
import os

import six
import yaml

from fuelclient.cli import error
from fuelclient import consts
from fuelclient import utils


class Serializer(object):
    """Serializer class - contains all logic responsible for
    printing to stdout, reading and writing files to file system.
    """
    serializers = {
        "json": {
            "w": lambda d: json.dumps(d, indent=4),
            "r": utils.safe_deserialize(json.loads)
        },
        "yaml": {
            "w": lambda d: yaml.safe_dump(d, default_flow_style=False),
            "r": utils.safe_deserialize(yaml.load)
        }
    }

    format_flags = False
    default_format = "yaml"
    format = default_format

    def __init__(self, format=None):
        if format and format in self.serializers:
            self.format = format
            self.format_flags = True

    @property
    def serializer(self):
        """Returns dicts with methods for loadin/dumping current fromat.

        Returned dict's keys:
            * 'w' - from 'writing', method for serializing/dumping data
            * 'r' - from 'reading', method for deserializing/loading data
        """
        return self.serializers[self.format]

    def serialize(self, data):
        """Shortcut for serializing data with current format."""
        return self.serializer['w'](data)

    def deserialize(self, data):
        """Shortcut for deserializing data with current format."""
        return self.serializer['r'](data)

    @classmethod
    def from_params(cls, params):
        return cls(format=getattr(params,
                                  consts.SERIALIZATION_FORMAT_FLAG, None))

    def print_formatted(self, data):
            print(self.serializer["w"](data))

    def print_to_output(self, formatted_data, arg, print_method=print):
        if self.format_flags:
            self.print_formatted(formatted_data)
        else:
            if six.PY2 and isinstance(arg, six.text_type):
                arg = arg.encode('utf-8')
            print_method(arg)

    def prepare_path(self, path):
        return "{0}.{1}".format(
            path, self.format
        )

    def write_to_path(self, path, data):
        full_path = self.prepare_path(path)
        return self.write_to_full_path(full_path, data)

    def write_to_full_path(self, path, data):
        try:
            with open(path, "w") as file_to_write:
                self.write_to_file(file_to_write, data)
        except IOError as e:
            raise error.InvalidFileException(
                "Can't write to file '{0}': {1}.".format(
                    path, e.strerror))
        return path

    def read_from_file(self, path):
        return self.read_from_full_path(self.prepare_path(path))

    def read_from_full_path(self, full_path):
        try:
            with open(full_path, "r") as file_to_read:
                return self.serializer["r"](file_to_read.read())
        except IOError as e:
            raise error.InvalidFileException(
                "Can't open file '{0}': {1}.".format(full_path, e.strerror))

    def write_to_file(self, file_obj, data):
        """Writes to opened file or file like object
        :param file_obj: opened file
        :param data: any serializable object
        """
        serialized = self.serializer["w"](data)
        file_obj.write(serialized)


class FileFormatBasedSerializer(Serializer):

    def get_serializer(self, path):
        extension = os.path.splitext(path)[1][1:]
        if extension not in self.serializers:
            raise error.BadDataException(
                'No serializer for provided file {0}'.format(path))
        return self.serializers[extension]

    def write_to_file(self, full_path, data):
        serializer = self.get_serializer(full_path)
        with open(full_path, "w+") as f:
            f.write(serializer["w"](data))
        return full_path

    def read_from_file(self, full_path):
        serializer = self.get_serializer(full_path)
        with open(full_path, "r") as f:
            return serializer["r"](f.read())


def listdir_without_extensions(dir_path):
    return six.moves.filter(
        lambda f: f != "",
        six.moves.map(
            lambda f: f.split(".")[0],
            os.listdir(dir_path)
        )
    )
