# -*- coding: UTF-8 -*-
from django.core.management.base import BaseCommand, CommandError
from django.core.management import call_command
from django.conf import settings
from iconolab.models import Collection, Image, ImageStats, Item, ItemMetadata, MetaCategory
from PIL import Image as ImagePIL
import os, csv, pprint, re, json

class Command(BaseCommand):
    help = "import images from a directory into the media folder and creates item and image objects"

    def add_arguments(self, parser):
        parser.add_argument("csv_path")
        parser.add_argument(
            '--jpeg-quality',
            dest='jpeg_quality',
            default=settings.IMG_JPG_DEFAULT_QUALITY,
            help='Jpeg default quality'

        )
        parser.add_argument(
            '--encoding',
            dest='encoding',
            default='utf-8',
            help='CSV file encoding'

        )
        parser.add_argument(
            '--collection-json',
            dest='collection_json',
            default=False,
            help='creates a new collection from a json file, must be an object with fields : '+ \
                 '"name" (identifier), '+ \
                 '"verbose_name" (proper title name), '+ \
                 '"description" (description on homepage, html is supported), '+ \
                 '"image" (image on homepages, must be "uploads/<imgname>"), '+ \
                 '"height" and "width" (height and width of the image)',
        )
        parser.add_argument(
            '--collection-id',
            dest='collection_id',
            default=False,
            help='insert extracted data into the specified collection instead of trying to load a collection fixture',
        )
        parser.add_argument(
            '--metacategories-json',
            dest='metacategories_json',
            default=False,
            help='add metacategories to the collection from a json file (json must be a list of object with "label" and "triggers_notifications" fields)',
        )
        parser.add_argument(
            '--delimiter',
            dest='csv_delimiter',
            default=';',
            help='csv file delimiter'
        )
        parser.add_argument(
            '--img-filename-identifier',
            dest='img_filename_identifier',
            default=settings.IMPORT_DEFAULT_FIELD_TO_FILENAME_IDENTIFIER,
            help='codename of the csv field we\'ll try to match to find the related image to a given object'
        )

    def handle(self, *args, **options):
        pp = pprint.PrettyPrinter(indent=4)
        try:
            # Check we have a collection to store data into:
            source_dir = os.path.dirname(os.path.realpath(options.get("csv_path")))
            print("# Checking collection args")
            if options.get("collection_json"):
                print("## Finding collection json data in "+source_dir)
                collection_json_path = os.path.join(source_dir, options.get("collection_json"))
                if not os.path.isfile(collection_json_path):
                    print("### No "+options.get("collection_json")+".json file was found in the source directory")
                    raise ValueError("!!! Json file "+collection_json_path+" was not found !!!")
                try:
                    with open(collection_json_path) as json_fixture_file:
                        collection_data = json.loads(json_fixture_file.read())
                        for key in ["name", "verbose_name", "description", "image", "height", "width"]:
                            if not key in collection_data.keys():
                                print("!!! Json file "+collection_json_path+" has no "+key+" field !!!")
                                raise ValueError()
                        if not collection_data.get("name", ""):
                            print("!!! Collection data key 'name' is empty")
                            raise ValueError()
                        if Collection.objects.filter(name=collection_data.get("name")).exists():
                            print("!!! A Collection with the provided name already exists!")
                            raise ValueError()
                        if collection_data.get("image", "") and not (collection_data.get("width", 0) and collection_data.get("height", 0)):
                            print("!!! Collection data has an image but no height and width")
                            raise ValueError()
                except ValueError as e:
                    raise ValueError("!!! JSON Data is invalid. !!!")
            elif options.get("collection_id"):
                print("## Finding collection with id "+options.get("collection_id"))
                try:
                    collection = Collection.objects.get(pk=options.get("collection_id"))
                except Collection.DoesNotExist:
                    raise ValueError("!!! Collection with primary key "+options.get("collection_id")+" was not found, aborting !!!")
            else:
                raise ValueError("!!! No collection fixture or collection id, aborting because we can't properly generate data. !!!")

            if options.get("metacategories_json"):
                print("## Finding metacategories fixture json data in "+source_dir)
                metacategories_json_path = os.path.join(source_dir, options.get("metacategories_json"))
                if not os.path.isfile(metacategories_json_path):
                    print("### No "+options.get("metacategories_json")+".json file was found in the source directory")
                    raise ValueError("!!! Fixture file "+metacategories_json_path+" was not found !!!")
                with open(metacategories_json_path) as metacategories_json_file:
                    metacategories_data = json.loads(metacategories_json_file.read())
                    for metacategory in metacategories_data:
                        if metacategory.get("label", None) is None:
                            raise ValueError("!!! Metacategory without label !!!")

            # We read the csv
            delimiter = options.get('csv_delimiter')
            if delimiter == "#9":
                delimiter = chr(9)
            if delimiter == "#29":
                delimiter = chr(29)
            if delimiter == "#30":
                delimiter = chr(30)
            if delimiter == "#31":
                delimiter = chr(31)
            csvreader = csv.DictReader(open(options.get("csv_path"), encoding=options.get("encoding")), delimiter=delimiter)
            print("# Extracting data from csv file and storing it in standardized format")
            # We store data using the Jocondelab keys, as defined in settings.IMPORT_FIELDS_DICT
            cleaned_csv_data=[]
            for row in csvreader:
                cleaned_row_data = {}
                for key in settings.IMPORT_FIELDS_DICT.keys():
                    cleaned_row_data[key] = ""
                    for row_key in row.keys():
                        if row_key in settings.IMPORT_FIELDS_DICT[key]:
                            cleaned_row_data[key] = row[row_key]
                            break
                cleaned_csv_data.append(cleaned_row_data)
            # Listing image files in csv directory
            image_list = [f for f in os.listdir(source_dir) if os.path.isfile(os.path.join(source_dir, f)) and not f.endswith(".csv")]
            filtered_csv_data = []
            # Now we trim the cleaned_csv_data dict to keep only entries that have at least one image
            for item in cleaned_csv_data:
                item["SRC_IMG_FILES"] = []
                has_image = False
                for image in image_list:                    
                    img_name_pattern = r'.*'+re.escape(item[options.get("img_filename_identifier")])+r'[\.\-_].*'
                    if re.match(img_name_pattern, image):
                        item["SRC_IMG_FILES"].append(image)
                        has_image = True
                if has_image:
                    filtered_csv_data.append(item)
            
            print("## found " + str(len(filtered_csv_data))+" items with at least one image")
            print("# Importing data into Iconolab")
            if options.get("collection_json"):
                print("## Loading collection json")
                collection = Collection.objects.create(
                    name = collection_data.get("name"),
                    verbose_name = collection_data.get("verbose_name", ""),
                    description = collection_data.get("description", ""),
                    image = collection_data.get("image", ""),
                    height = collection_data.get("height", 0),
                    width = collection_data.get("width", 0),
                )
                if collection.image:
                    collection_image_path = os.path.join(settings.MEDIA_ROOT, str(collection.image))
                    if not os.path.isfile(collection_image_path):
                        print("### Moving collection image")
                        _ , collection_image_name = os.path.split(collection_image_path)
                        try:
                            col_im = ImagePIL.open(os.path.join(source_dir, collection_image_name))
                            print("##### Generating or copying jpeg for "+collection_image_name)
                            col_im.thumbnail(col_im.size)
                            col_im.save(collection_image_path, "JPEG", quality=options.get("jpeg_quality", settings.IMG_JPG_DEFAULT_QUALITY))
                        except Exception as e:
                            print(e)
            if options.get("metacategories_json"):
                for metacategory in metacategories_data:
                    MetaCategory.objects.create(
                        collection = collection,
                        label = metacategory.get("label"),
                        triggers_notifications = metacategory.get("triggers_notifications", 0)
                    )
            print("## Converting image and moving it to static dir, creating Image and Item objects")
            target_dir = os.path.join(settings.MEDIA_ROOT, "uploads")
            print("### Images will be stored in "+target_dir)
            for item in filtered_csv_data:
                print("#### Computing metadatas for item "+item["INV"]+" (inv number)")
                if not item["INV"]:
                    print("#### No INV number, skipping")
                    continue
                item_authors = item["AUTR"]
                item_school = item["ECOLE"]
                item_designation = ""
                if item.get("TITR", ""):
                    item_designation = item["TITR"]
                elif item.get("DENO", ""):
                    item_designation = item["DENO"]
                elif item.get("APPL", ""):
                    item_designation = item["APPL"]
                item_datation = ""
                if item.get("PERI", ""):
                    item_datation = item["PERI"]
                elif item.get("MILL", ""):
                    item_datation = item["MILL"]
                elif item.get("EPOQ", ""):
                    item_datation = item["EPOQ"]
                item_technics = item["TECH"]
                item_measurements = item["DIMS"]
                item_create_or_usage_location = item["LIEUX"]
                item_discovery_context = item["DECV"]
                item_conservation_location = item["LOCA"]
                item_photo_credits = item["PHOT"]
                item_inventory_number = item["INV"]
                item_joconde_ref = item["REF"]
                if ItemMetadata.objects.filter(item__collection = collection, inventory_number = item_inventory_number).exists():
                    print("#### An item with "+item["INV"]+" for inventory number, already exists in databse in the import collection")
                else:
                    print("#### Creating item "+item["INV"]+" (inv number) in databse")
                    item_object = Item.objects.create(
                        collection = collection
                    )
                    ItemMetadata.objects.create(
                        item = item_object,
                        authors = item_authors,
                        school = item_school,
                        designation = item_designation,
                        datation = item_datation,
                        technics = item_technics,
                        measurements = item_measurements,
                        create_or_usage_location = item_create_or_usage_location,
                        discovery_context = item_discovery_context,
                        conservation_location = item_conservation_location,
                        photo_credits = item_photo_credits,
                        inventory_number = item_inventory_number,
                        joconde_ref = item_joconde_ref
                    )
                    print("#### Computing item image(s)")
                    for image in item["SRC_IMG_FILES"]:
                        (image_name, ext) = os.path.splitext(image)
                        image_path = os.path.join(target_dir, image_name) + ".jpg"
                        if os.path.isfile(image_path):
                            print("##### A jpeg file already exists in target dir for "+ image)
                            try:
                                im = ImagePIL.open(image_path)
                                im_width, im_height = im.size
                            except Exception as e:
                                print(e)
                                continue
                        else:
                            jpeg_img_path = image_path
                            try:
                                im = ImagePIL.open(os.path.join(source_dir, image))
                                print("##### Generating or copying jpeg for "+image)
                                im.thumbnail(im.size)
                                im.save(jpeg_img_path, "JPEG", quality=options.get("jpeg_quality", settings.IMG_JPG_DEFAULT_QUALITY))
                                im_width, im_height = im.size
                            except Exception as e:
                                print(e)
                                continue
                        new_image = Image.objects.create(
                            item = item_object,
                            media = "uploads/"+image_name+".jpg",
                            name = image_name+".jpg",
                            height = im_height,
                            width = im_width
                        )
                        ImageStats.objects.create(
                            image = new_image
                        )
            print("# All done!")
        except FileNotFoundError:
            print("!!! File "+options.get("csv_path")+" does not exist. !!!")
        except ValueError as e:
            print(str(e))
