/* UtilLib - Copyright (c) 2008-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.primitives;

import java.text.ParseException;


/**
 *
 * @author Vincent Calame
 */
public final class DegreSexagesimal {

    private final boolean positif;
    private final int degre;
    private final int minute;
    private final int seconde;

    private DegreSexagesimal(boolean positif, int degre, int minute, int seconde) {
        this.positif = positif;
        this.degre = degre;
        this.minute = minute;
        this.seconde = seconde;
    }

    public int getDegre() {
        return degre;
    }

    public int getMinute() {
        return minute;
    }

    public int getSeconde() {
        return seconde;
    }

    public boolean isPositif() {
        return positif;
    }

    public static DegreSexagesimal fromDegreDecimal(DegreDecimal degreDecimal) {
        float f = Decimal.toFloat(degreDecimal.getZeroLength(), degreDecimal.getPartieDecimale());
        float minF = (f * 60);
        int min = (int) Math.floor(minF);
        float secF = (minF - min) * 60;
        int sec = (int) Math.round(secF);
        if (sec == 60) {
            sec = 0;
            min = min + 1;
            if (min == 60) {
                min = 59;
                sec = 59;
            }
        }
        return new DegreSexagesimal(degreDecimal.isPositif(), Math.abs(degreDecimal.getPartieEntiere()), min, sec);
    }

    @Override
    public String toString() {
        return toString(true, "");
    }

    public String toString(boolean withSign, String separator) {
        StringBuilder buf = new StringBuilder();
        if (withSign) {
            if (!positif) {
                buf.append("-");
            }
        }
        buf.append(degre);
        buf.append('°');
        buf.append(separator);
        if (minute < 10) {
            buf.append('0');
        }
        buf.append(minute);
        buf.append('\u2032');
        buf.append(separator);
        if (seconde < 10) {
            buf.append('0');
        }
        buf.append(seconde);
        buf.append('\u2033');
        return buf.toString();
    }

    public DegreSexagesimal getOppose() {
        if ((minute == 0) && (seconde == 0) && ((degre == 0) || (degre == 180))) {
            return this;
        }
        return new DegreSexagesimal(!positif, degre, minute, seconde);
    }

    public static DegreSexagesimal parse(String s) throws ParseException {
        s = s.trim();
        int length = s.length();
        if (length == 0) {
            throw new ParseException("empty", 0);
        }
        char carac = s.charAt(0);
        boolean positif = true;
        if (carac == '-') {
            positif = false;
            s = s.substring(1).trim();
            length = s.length();
            if (length == 0) {
                throw new ParseException("no digit", 0);
            }
        }
        int degre = 0;
        int minute = 0;
        int seconde = 0;
        int nextIndex = 0;
        boolean valide = false;
        boolean debut = true;
        for (int i = 0; i < length; i++) {
            nextIndex = i + 1;
            int integer = toInt(s.charAt(i));
            if (integer == -2) {
                continue;
            } else if (integer == -1) {
                if (!valide) {
                    throw new ParseException("no digit", 0);
                } else {
                    break;
                }
            } else {
                valide = true;
                if (debut) {
                    if (integer == 0) {
                        continue;
                    } else {
                        debut = false;
                    }
                }
                degre = degre * 10 + integer;
            }
        }
        if (nextIndex < length) {
            for (int i = nextIndex; i < length; i++) {
                if (toInt(s.charAt(i)) < 0) {
                    nextIndex = i + 1;
                } else {
                    break;
                }
            }
        }
        if (nextIndex < length) {
            debut = true;
            for (int i = nextIndex; i < length; i++) {
                nextIndex = i + 1;
                int integer = toInt(s.charAt(i));
                if (integer == -2) {
                    continue;
                } else if (integer == -1) {
                    break;
                } else {
                    if (debut) {
                        if (integer == 0) {
                            continue;
                        } else {
                            debut = false;
                        }
                    }
                    minute = minute * 10 + integer;
                }
            }
        }
        if (nextIndex < length) {
            for (int i = nextIndex; i < length; i++) {
                if (toInt(s.charAt(i)) < 0) {
                    nextIndex = i + 1;
                } else {
                    break;
                }
            }
        }
        if (nextIndex < length) {
            debut = true;
            for (int i = nextIndex; i < length; i++) {
                nextIndex = i + 1;
                int integer = toInt(s.charAt(i));
                if (integer == -2) {
                    continue;
                } else if (integer == -1) {
                    break;
                } else {
                    if (debut) {
                        if (integer == 0) {
                            continue;
                        } else {
                            debut = false;
                        }
                    }
                    seconde = seconde * 10 + integer;
                }
            }
        }
        int modulo = seconde / 60;
        seconde = seconde - (60 * modulo);
        minute = minute + modulo;
        modulo = minute / 60;
        minute = minute - (60 * modulo);
        degre = degre + modulo;
        degre = degre % 360;
        if ((degre > 180) || ((degre == 180) & ((!positif) || ((minute == 0) && (seconde == 0))))) {
            minute = 60 - minute;
            seconde = 60 - seconde;
            degre = 360 - 180;
            positif = !positif;
        }
        return new DegreSexagesimal(positif, degre, minute, seconde);
    }

    private static int toInt(char carac) {
        if (carac == ' ') {
            return -2;
        }
        if ((carac < '0') || (carac > '9')) {
            return -1;
        }
        return ((int) carac) - 48;
    }

}
