/**
 * 
 */
package org.forkalsrud.album.exif;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import org.forkalsrud.album.db.DirectoryDatabase;
import org.forkalsrud.album.db.DirectoryProps;

/**
 * @author knut
 */
public class DirectoryEntry extends EntryWithChildren<Entry> {

    final static String OVERRIDE_FILE = "album.properties";

    public interface ServiceApi {

        DirectoryDatabase getDirectoryDatabase();
        DirectoryMetadataGenerator getMetadataGenerator();
    }

    ServiceApi services;
    DirectoryProps cache;
    boolean childrenLoaded = false;
    Comparator<Entry> ordering = null;
    Date earliest = null;
    boolean groupByYear;

    public DirectoryEntry(ServiceApi api, File file) {
        super(file);
        if (!file.isDirectory()) {
            throw new RuntimeException("Use DirectoryEntry only for directories: " + file);
        }
        this.services = api;
        cache = this.services.getDirectoryDatabase().load(file.getAbsolutePath());
        if (cache == null) {
            cache = new DirectoryProps();
        }
    }

    public DirectoryEntry(ServiceApi api, Entry parent, File file) {
        this(api, file);
        this.parent = parent;
    }


    @Override
    public List<Entry> getContents() {

        loadChildren();
        return super.getContents();
    }

    @Override
    public Thumbnail getThumbnail() {
        loadChildren();
        return super.getThumbnail();
    }

    @Override
    public String getCaption() {
        loadChildren();
        return super.getCaption();
    }

    @Override
    public Entry get(File f) {
        loadChildren();
        return super.get(f);
    }

    protected synchronized void loadChildren() {

        if (childrenLoaded) {
            return;
        }
        try {
            DirectoryProps cachedProps = isCacheCurrent() ? cache : generateCache();
            DirectoryProps combined = new DirectoryProps();
            combined.putAll(cachedProps);
            File override = new File(file, OVERRIDE_FILE);
            if (override.exists() && override.isFile() && override.canRead())  {
                DirectoryProps overrideProps = loadFromFile(override);
                combined.putAll(overrideProps);
            }
            populate(combined);
            sort();
            fillLinkedList();
            childrenLoaded = true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    boolean isCacheCurrent() {
        return file.lastModified() <= cache.getTimestamp();
    }

    DirectoryProps loadFromFile(File propFile) throws IOException {

        DirectoryProps props = new DirectoryProps();
        FileInputStream fis = new FileInputStream(propFile); 
        props.load(fis);
        fis.close();
        props.setTimestamp(propFile.lastModified());
        return props;
    }

    DirectoryProps generateCache() throws IOException, InterruptedException {

        DirectoryProps props = services.getMetadataGenerator().generate(file);
        services.getDirectoryDatabase().store(file.getAbsolutePath(), props);
        cache = props;
        return props;
    }


    private void sort() {

        children.sort(ordering);
    }



    private void populate(DirectoryProps props) throws ParseException {

        String coverFileName = props.getProperty("cover");
        String caption = props.getProperty("caption");
        setCaption(caption);
        ordering = ComparatorFactory.getSort(props.getProperty("sort"));
        
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");

        for (String key : props.stringPropertyNames()) {
            if (key.startsWith("file.") && key.endsWith(".dimensions")) {
                String name = key.substring("file.".length(), key.length() - ".dimensions".length());
                File f = new File(file, name);
                String type = props.getProperty("file." + name + ".type", "image");
                FileEntry entry = new FileEntry(this, f, type);
                Thumbnail thumbnail = new Thumbnail(f, type);
                entry.setCaption(props.getProperty("file." + name + ".caption"));
                Date fileDate = sdf.parse(props.getProperty("file." + name + ".captureDate"));
                entry.setDate(fileDate);
                thumbnail.setSize(new Dimension(props.getProperty("file." + name + ".dimensions")));
                thumbnail.setOrientation(Integer.parseInt(props.getProperty("file." + name + ".orientation")));
                thumbnail.setEtag(props.getProperty("file." + name + ".etag"));
                entry.setThumbnail(thumbnail);
                boolean hidden = Boolean.parseBoolean(props.getProperty("file." + name + ".hidden"));
                if (!hidden) {
                    children.add(entry);
                    if (name.equals(coverFileName)) {
                        setThumbnail(thumbnail);
                    }
                }
                String duration = props.getProperty("file." + name + ".length");
                if (duration != null) {
                    thumbnail.setDuration(duration);
                }
            } else if (key.startsWith("dir.") && !key.endsWith(".hidden") && !key.endsWith(".caption")) {
                String name = key.substring("dir.".length());
                boolean hidden = Boolean.parseBoolean(props.getProperty("dir." + name + ".hidden"));
                if (!hidden) {
                    DirectoryEntry dir = new DirectoryEntry(services, this, new File(file, name));
                    if (name.equals(coverFileName)) {
                        setThumbnail(dir.getThumbnail());
                    }
                    Date fileDate = dir.getEarliest();
                    dir.setDate(fileDate);
                    dir.setCaption(props.getProperty("dir." + name + ".caption"));
                    if (!dir.getContents().isEmpty()) {
                        children.add(dir);
                    }
                }
            }
        }
        this.earliest = children.stream()
                .map(Entry::getDate)
                .min(Date::compareTo)
                .orElse(new Date(file.lastModified()));
        this.groupByYear = "year".equalsIgnoreCase(props.getProperty("group"));
        if (thumbnail == null && !children.isEmpty()) {
            setThumbnail(children.get(0).getThumbnail());
        }
    }



    @Override
    public Date getEarliest() {
        loadChildren();
        return earliest;
    }

    @Override
    public boolean groupByYear() {
        return this.groupByYear;
    }


    public DirectoryProps getCache() {
        return cache;
    }
}
