/* FichothequeLib_Impl - Copyright (c) 2010-2022 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.impl;

import java.text.ParseException;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import net.fichotheque.ExistingNameException;
import net.fichotheque.FichothequeEditor;
import net.fichotheque.Metadata;
import net.fichotheque.album.Album;
import net.fichotheque.album.AlbumConstants;
import net.fichotheque.album.AlbumEditor;
import net.fichotheque.album.metadata.AlbumDim;
import net.fichotheque.album.metadata.AlbumMetadata;
import net.fichotheque.album.metadata.AlbumMetadataEditor;
import net.fichotheque.utils.AlbumUtils;
import net.mapeadores.util.awt.ResizeInfo;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.text.Label;


/**
 *
 * @author Vincent Calame
 */
class AlbumMetadataImpl extends AbstractMetadata implements AlbumMetadata {

    private final static ResizeInfo MINI_RESIZEINFO = ResizeInfo.newMaxDimInstance(96, 64);
    private final AlbumImpl album;
    private final SortedMap<String, AlbumDimImpl> albumDimMap = new TreeMap<String, AlbumDimImpl>();
    private List<AlbumDim> cacheList;

    private AlbumMetadataImpl(AlbumImpl album) {
        this.album = album;
    }

    static AlbumMetadataImpl.InitEditor fromInit(AlbumImpl.InitEditor albumInitEditor) {
        AlbumMetadataImpl metadata = new AlbumMetadataImpl((AlbumImpl) albumInitEditor.getAlbum());
        return new AlbumMetadataImpl.InitEditor(metadata, albumInitEditor);
    }

    static AlbumMetadataImpl fromNew(AlbumImpl album) {
        return new AlbumMetadataImpl(album);
    }

    @Override
    public Album getAlbum() {
        return album;
    }

    @Override
    public List<AlbumDim> getAlbumDimList() {
        List<AlbumDim> result = cacheList;
        if (result == null) {
            result = initAlbumDimList();
        }
        return result;
    }

    @Override
    public AlbumDim getAlbumDimByName(String name) {
        return albumDimMap.get(name);
    }

    @Override
    public ResizeInfo getResizeInfo(short specialDim) {
        switch (specialDim) {
            case AlbumConstants.MINI_SPECIALDIM:
                return MINI_RESIZEINFO;
            default:
                return null;
        }
    }

    @Override
    protected void fireChange(FichothequeEditor fichothequeEditor) {
        AlbumImpl.AlbumEditorImpl albumEditor = (AlbumImpl.AlbumEditorImpl) fichothequeEditor.getAlbumEditor(album);
        albumEditor.setMetadataChange();
    }

    AlbumMetadataEditor getAlbumMetadataEditor(AlbumImpl.AlbumEditorImpl albumEditor) {
        return new AlbumMetadataEditorImpl(albumEditor);
    }

    boolean isFromAlbum(AlbumDim albumDim) {
        try {
            testAlbumDim(albumDim);
            return true;
        } catch (IllegalArgumentException iae) {
            return false;
        }
    }

    void clear() {
        albumDimMap.clear();
        clearCache();
    }

    private AlbumDimImpl testAlbumDim(AlbumDim albumDim) throws IllegalArgumentException {
        if (!(albumDim instanceof AlbumDimImpl)) {
            throw new IllegalArgumentException("albumDim argument does not come from this AlbumMetadata instance");
        }
        if (albumDim.getAlbumMetadata() != this) {
            throw new IllegalArgumentException("albumDim argument does not come from this AlbumMetadata instance");
        }
        return (AlbumDimImpl) albumDim;
    }

    private synchronized AlbumDim innerCreateAlbumDim(String name, String type) throws ExistingNameException, ParseException {
        if (name == null) {
            throw new IllegalArgumentException("name is null");
        }
        if (name.length() == 0) {
            throw new IllegalArgumentException("name is empty");
        }
        if (albumDimMap.containsKey(name)) {
            throw new ExistingNameException();
        }
        AlbumUtils.testAlbumDimName(name);
        AlbumDimImpl bdfimplAlbumDim = new AlbumDimImpl(name, type);
        albumDimMap.put(name, bdfimplAlbumDim);
        clearCache();
        return bdfimplAlbumDim;
    }

    private synchronized boolean innerSetDim(AlbumDim albumDim, int width, int height) {
        AlbumDimImpl bdfimplAlbumDim = testAlbumDim(albumDim);
        boolean done = bdfimplAlbumDim.setDim(width, height);
        return done;
    }

    private synchronized boolean innerRemoveAlbumDim(AlbumDim albumDim) {
        AlbumDimImpl bdfimplAlbumDim = testAlbumDim(albumDim);
        String name = bdfimplAlbumDim.getName();
        if (!albumDimMap.containsKey(name)) {
            return false;
        }
        albumDimMap.remove(name);
        clearCache();
        return true;
    }

    private void clearCache() {
        cacheList = null;
    }

    private synchronized List<AlbumDim> initAlbumDimList() {
        List<AlbumDim> list = AlbumUtils.wrap(albumDimMap.values().toArray(new AlbumDim[albumDimMap.size()]));
        cacheList = list;
        return list;
    }


    static class InitEditor implements AlbumMetadataEditor {

        private final AlbumMetadataImpl albumMetadata;
        private final AlbumEditor albumEditor;

        private InitEditor(AlbumMetadataImpl albumMetadata, AlbumEditor albumEditor) {
            this.albumMetadata = albumMetadata;
            this.albumEditor = albumEditor;
        }

        @Override
        public AlbumMetadata getAlbumMetadata() {
            return albumMetadata;
        }

        @Override
        public AlbumEditor getAlbumEditor() {
            return albumEditor;
        }

        @Override
        public AlbumDim createAlbumDim(String name, String type) throws ExistingNameException, ParseException {
            return albumMetadata.innerCreateAlbumDim(name, type);
        }

        @Override
        public boolean removeAlbumDim(AlbumDim albumDim) {
            throw new UnsupportedOperationException("Not during init");
        }

        @Override
        public boolean setDim(AlbumDim albumDim, int width, int height) {
            return albumMetadata.innerSetDim(albumDim, width, height);
        }

        @Override
        public Metadata getMetadata() {
            return albumMetadata;
        }

        @Override
        public boolean putLabel(String name, Label label) {
            return albumMetadata.innerPutLabel(name, label);
        }

        @Override
        public boolean removeLabel(String name, Lang lang) {
            return albumMetadata.innerRemoveLabel(name, lang);
        }

    }


    private class AlbumMetadataEditorImpl implements AlbumMetadataEditor {

        private final AlbumImpl.AlbumEditorImpl albumEditor;

        private AlbumMetadataEditorImpl(AlbumImpl.AlbumEditorImpl albumEditor) {
            this.albumEditor = albumEditor;
        }

        @Override
        public AlbumMetadata getAlbumMetadata() {
            return AlbumMetadataImpl.this;
        }

        @Override
        public AlbumEditor getAlbumEditor() {
            return albumEditor;
        }

        @Override
        public AlbumDim createAlbumDim(String name, String type) throws ExistingNameException, ParseException {
            AlbumDim albumDim = innerCreateAlbumDim(name, type);
            fireChange();
            return albumDim;
        }

        @Override
        public boolean removeAlbumDim(AlbumDim albumDim) {
            String name = albumDim.getName();
            boolean done = innerRemoveAlbumDim(albumDim);
            if (done) {
                AlbumDataSource albumDataSource = ((FichothequeImpl) album.getFichotheque()).getFichothequeDataSource().getAlbumDataSource();
                albumDataSource.clearDimCache(album.getSubsetKey(), name);
                fireChange();
            }
            return done;
        }

        @Override
        public boolean setDim(AlbumDim albumDim, int width, int height) {
            boolean done = innerSetDim(albumDim, width, height);
            if (done) {
                AlbumDataSource albumDataSource = ((FichothequeImpl) album.getFichotheque()).getFichothequeDataSource().getAlbumDataSource();
                albumDataSource.clearDimCache(album.getSubsetKey(), albumDim.getName());
                fireChange();
            }
            return done;
        }

        @Override
        public Metadata getMetadata() {
            return AlbumMetadataImpl.this;
        }

        @Override
        public boolean putLabel(String name, Label label) {
            boolean done = innerPutLabel(name, label);
            if (done) {
                fireChange();
            }
            return done;
        }

        @Override
        public boolean removeLabel(String name, Lang lang) {
            boolean done = innerRemoveLabel(name, lang);
            if (done) {
                fireChange();
            }
            return done;
        }

        private void fireChange() {
            albumEditor.setMetadataChange();
        }

    }


    private class AlbumDimImpl implements AlbumDim {

        private final String name;
        private final String type;
        private ResizeInfo resizeInfo = MINI_RESIZEINFO;

        private AlbumDimImpl(String name, String type) {
            this.name = name;
            this.type = type;
            resizeInfo = toResizeInfo(type, 100, 100);
        }

        @Override
        public AlbumMetadata getAlbumMetadata() {
            return AlbumMetadataImpl.this;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getDimType() {
            return type;
        }

        @Override
        public ResizeInfo getResizeInfo() {
            return resizeInfo;
        }

        private boolean setDim(int width, int height) {
            try {
                ResizeInfo newResizeInfo = toResizeInfo(type, width, height);
                if (newResizeInfo.equals(resizeInfo)) {
                    return false;
                }
                this.resizeInfo = newResizeInfo;
                return true;
            } catch (IllegalArgumentException iae) {
                return false;
            }
        }

    }

    private static ResizeInfo toResizeInfo(String type, int width, int height) {
        switch (type) {
            case AlbumConstants.FIXEDWIDTH_DIMTYPE:
                return ResizeInfo.newFixedWidthInstance(width);
            case AlbumConstants.FIXEDHEIGHT_DIMTYPE:
                return ResizeInfo.newFixedHeightInstance(height);
            case AlbumConstants.MAXWIDTH_DIMTYPE:
                return ResizeInfo.newMaxWidthInstance(width);
            case AlbumConstants.MAXHEIGHT_DIMTYPE:
                return ResizeInfo.newMaxHeightInstance(height);
            case AlbumConstants.MAXDIM_DIMTYPE:
                return ResizeInfo.newMaxDimInstance(width, height);
            default:
                throw new SwitchException("unknownType = " + type);
        }
    }

}
