/* UtilLib - Copyright (c) 2006-2015 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.mapeadores.util.net;

import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.ParseException;


/**
 * Les URI basé sur un UUID sont de forme suivante schéma:/{UUID du
 * producteur}/{nom d'identification propre au producteur}/{chemin}. La longueur
 * du chemin est propre à cahque schéma, mais un schéma comporte un nombre de
 * parties fixes. Par exemple, un schéma indiquera qu'il y a trois parties dans
 * le chemin après /{UUID du producteur}/{nom d'identification Par exemple, le
 * schéma d'un descripteur est descripteur:/{UUID du producteur}/{nom
 * d'identification propre au producteur}/{iddesc} Chaque partie est codée en
 * UTF-8 de la même manière qu'un url. Un URI ne termine jamais par un slash '/
 *
 */
/**
 *
 * @author Vincent Calame
 */
public class UUIDURI {

    private String scheme;
    private String authorityUUID = null;
    private String internalName = null;
    private String[] idPartArray;

    public UUIDURI(String scheme, String authorityUUID, String internalName, String[] idPartArray) {
        if (scheme == null) {
            throw new IllegalArgumentException("scheme is null");
        }
        this.scheme = scheme;
        if (authorityUUID != null) {
            if (internalName == null) {
                throw new IllegalArgumentException("internalName is null when authorityUUID is not null");
            }
            this.internalName = internalName;
            this.authorityUUID = authorityUUID;
        }
        if (idPartArray == null) {
            this.idPartArray = new String[0];
        } else {
            this.idPartArray = idPartArray;
        }
    }

    public UUIDURI(String scheme, String[] idPartArray) {
        this.scheme = scheme;
        this.idPartArray = idPartArray;
    }

    public boolean isAbsolute() {
        return (authorityUUID != null);
    }

    public String getScheme() {
        return scheme;
    }

    public String getAuthorityUUID() {
        return authorityUUID;
    }

    public String getInternalName() {
        return internalName;
    }

    public String[] getIdPartArray() {
        return idPartArray;
    }

    public int getIdPartCount() {
        return idPartArray.length;
    }

    public String getIdPart(int i) {
        return idPartArray[i];
    }

    @Override
    public String toString() {
        URIBuilder buf = new URIBuilder(scheme, authorityUUID, internalName);
        for (String idPart : idPartArray) {
            buf.appendIdPart(idPart);
        }
        return buf.toString();
    }

    public static UUIDURI parse(String s) throws ParseException {
        int idx = s.indexOf(':');
        if (idx == -1) {
            throw new ParseException("scheme is missing in " + s, s.length());
        }
        String scheme = s.substring(0, idx);
        String path = s.substring(idx + 1);
        if (path.length() == 0) {
            throw new ParseException("path is missing in " + s, s.length());
        }
        if (path.charAt(path.length() - 1) == '/') {
            throw new ParseException("path ends with / : " + s, s.length() - 1);
        }
        if (path.charAt(0) == '/') {
            return parseAbsolute(scheme, path);
        } else {
            return parseRelative(scheme, path);
        }
    }

    public static String toString(String scheme, String idpart1) {
        URIBuilder buf = new URIBuilder(scheme, null, null);
        buf.appendIdPart(idpart1);
        return buf.toString();
    }

    public static String toString(String scheme, String idpart1, String idpart2) {
        URIBuilder buf = new URIBuilder(scheme, null, null);
        buf.appendIdPart(idpart1);
        buf.appendIdPart(idpart2);
        return buf.toString();
    }

    public static String toString(String scheme, String idpart1, String idpart2, String idpart3) {
        URIBuilder buf = new URIBuilder(scheme, null, null);
        buf.appendIdPart(idpart1);
        buf.appendIdPart(idpart2);
        buf.appendIdPart(idpart3);
        return buf.toString();
    }

    private static UUIDURI parseAbsolute(String scheme, String path) throws ParseException {
        String[] tokens = path.split("/");
        if (tokens.length < 2) {
            throw new ParseException("absolute must have an minimum of two parts " + path, path.length() - 1);
        }
        String authorityUUID = tokens[1];
        String internalName;
        try {
            internalName = URLDecoder.decode(tokens[2], "UTF-8");
        } catch (IOException ieo) {
            throw new ParseException("ioe exception occurs", 0);
        }
        int length = tokens.length;
        String[] idPartArray = new String[length - 2];
        for (int i = 0; i < (length - 2); i++) {
            try {
                idPartArray[i] = URLDecoder.decode(tokens[i + 2], "UTF-8");
            } catch (IOException ieo) {
                throw new ParseException("ioe exception occurs", 0);
            }
        }
        return new UUIDURI(scheme, authorityUUID, internalName, idPartArray);
    }

    private static UUIDURI parseRelative(String scheme, String path) throws ParseException {
        String[] tokens = path.split("/");
        int length = tokens.length;
        String[] idPartArray = new String[length];
        for (int i = 0; i < length; i++) {
            try {
                idPartArray[i] = URLDecoder.decode(tokens[i], "UTF-8");
            } catch (IOException ieo) {
                throw new ParseException("ioe exception occurs", 0);
            }
        }
        return new UUIDURI(scheme, idPartArray);
    }


    private static class URIBuilder {

        private StringBuilder buf;
        private boolean relativePart = true;

        private URIBuilder(String scheme, String authorityUUID, String internalName) {
            buf = new StringBuilder();
            buf.append(scheme);
            buf.append(':');
            if (authorityUUID != null) {
                buf.append('/');
                buf.append(authorityUUID);
                buf.append('/');
                try {
                    buf.append(URLEncoder.encode(internalName, "UTF-8"));
                } catch (IOException ioe) {
                    throw new IllegalStateException("should not occur", ioe);
                }
                relativePart = false;
            }
        }

        @Override
        public String toString() {
            return buf.toString();
        }

        private void appendIdPart(String idpart) {
            if (relativePart) {
                relativePart = false;
            } else {
                buf.append('/');
            }
            try {
                buf.append(URLEncoder.encode(idpart, "UTF-8"));
            } catch (IOException ioe) {
                throw new IllegalStateException("should not occur", ioe);
            }
        }

    }

}
