#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# IDE: PyCharm 2017.2.4
# Author: Dajiang Ren<rendajiang@gagogroup.com>
# Created on 2018-09-26

import os
import shutil
import datetime

from . import StorageService, Bucket, Object, ObjectNotExistsError, ObjectExistsError


class FileSystemObject(Object):
    def __init__(self, bucket_name: str, object_name: str, size: int, e_tag: str, last_modified: datetime.datetime,
                 directory: str):
        super().__init__(bucket_name, object_name, size, e_tag, last_modified)
        self.full_path = os.path.join(directory, object_name)

    def _download_to_file(self, local_file_path: str):
        local_dir: str = os.path.dirname(local_file_path)
        if local_dir and not os.path.exists(local_dir):
            os.makedirs(local_dir, exist_ok=True)
        shutil.copy(self.full_path, local_file_path)

    def get_url(self):
        return self.full_path

    def get_gdal_url(self):
        return self.full_path


class FileSystemBucket(Bucket):
    def __init__(self, bucket_name: str, root: str):
        super().__init__(bucket_name)
        self.directory = os.path.join(root, bucket_name)
        if not os.path.exists(self.directory):
            os.makedirs(self.directory, exist_ok=True)

    def get_object_list(self, prefix: str=None) -> list:
        file_list = list()
        files = os.listdir(self.directory)
        for f in files:
            full_path: str = os.path.join(self.directory, f)
            if os.path.isdir(full_path):
                pass
            elif os.path.isfile(full_path):
                file_list.append(f)
        return files

    def get_object(self, object_name: str) -> Object or None:
        if self.exist(object_name):
            full_path = os.path.join(self.directory, object_name)
            return FileSystemObject(self.bucket_name, object_name, os.path.getsize(full_path), '',
                                    os.path.getmtime(full_path), self.directory)

    def upload_file(self, local_file_path: str, object_name: str, acl: str, overwrite_if_exists: bool=True) -> Object:
        if not os.path.exists(local_file_path):
            raise FileNotFoundError
        if self.exist(object_name):
            if overwrite_if_exists:
                self.remove(object_name)
            else:
                raise ObjectExistsError
        dst_path = os.path.join(self.directory, object_name)
        dir_name = os.path.dirname(dst_path)
        os.makedirs(dir_name, exist_ok=True)
        shutil.copy(local_file_path, dst_path)
        return self.get_object(object_name)

    def upload_data(self, data: bytes, object_name: str, acl: str, overwrite_if_exists: bool=True) -> Object:
        if self.exist(object_name):
            if overwrite_if_exists:
                self.remove(object_name)
            else:
                raise ObjectExistsError
        dst_path = os.path.join(self.directory, object_name)
        dir_name = os.path.dirname(dst_path)
        os.makedirs(dir_name, exist_ok=True)
        with open(dst_path, 'wb') as fp:
            fp.write(data)
        return self.get_object(object_name)

    def exist(self, object_name: str) -> bool:
        full_path = os.path.join(self.directory, object_name)
        return os.path.exists(full_path)

    def remove(self, object_name: str):
        if self.exist(object_name):
            full_path = os.path.join(self.directory, object_name)
            os.remove(full_path)
        else:
            raise ObjectNotExistsError

    def batch_remove(self, object_names: list):
        for o_n in object_names:
            self.remove(o_n)

    def rename(self, object_name: str, new_object_name: str, acl: str='private'):
        if self.exist(object_name):
            src_path: str = os.path.join(self.directory, object_name)
            dst_path: str = os.path.join(self.directory, new_object_name)
            shutil.move(src_path, dst_path)
        else:
            raise ObjectNotExistsError


class FileSystemStorageService(StorageService):
    def __init__(self, first: str, second: str, region: str=''):
        super().__init__(first, second)
        self.root = region

    def _get_bucket_impl(self, bucket_name: str):
        return FileSystemBucket(bucket_name, self.root)


def create_service(first: str, second: str, region: str=''):
    return FileSystemStorageService(first, second, region)


if __name__ == '__main__':
    f: StorageService = FileSystemStorageService('', '')
    b: Bucket = f.get_bucket('123')
    b.upload_file('s3.py', '123.py', acl='public', overwrite_if_exists=True)
