package uk.me.nxg.unity;

import java.util.regex.Pattern;
import java.util.regex.Matcher;


/**
 * A unit which has a prefix which is a round power of 1000.
 * @see SimpleBinaryUnit
 */
public class SimpleDecimalUnit extends SimpleUnit {
    /**
     * A regexp to match a unit with an SI prefix.
     * The order in this regexp is important – the "da" must match
     * before "d" can, so that "dam" is parsed as "da"+"m", and not
     * "d"+"am".  Also, this regexp requires there to be a unit: "m"
     * is not parsed as a prefix plus an empty unit.
     */
    private static final Pattern unitWithPrefix
            = Pattern.compile("(da|[qryzafpnumcdhkMGTPEZYRQ])(.+)");

    private final boolean unitWasGuessed;

    SimpleDecimalUnit(UnitDefinitionMap.Resolver r,
                      int prefixPower,
                      String us,
                      UnitDefinition ud,
                      float e) {
        super(r, prefixPower, us, ud, e, false);
        unitWasGuessed = false;
    }

    SimpleDecimalUnit(UnitDefinitionMap.Resolver r,
                      int prefixPower,
                      String us,
                      UnitDefinition ud,
                      float e,
                      boolean quoted,
                      boolean guessed_p) {
        super(r, prefixPower, us, ud, e, quoted);
        unitWasGuessed = guessed_p;
    }

    @Override SimpleDecimalUnit reciprocate() {
        return new SimpleDecimalUnit(getUnitResolver(),
                                     getPrefix(),
                                     getBaseUnitString(),
                                     getBaseUnitDefinition(),
                                     -getExponent(),
                                     isQuoted(),
                                     unitWasGuessed);
    }

    @Override SimpleDecimalUnit pow(double exponent) {
        return new SimpleDecimalUnit(getUnitResolver(),
                                     getPrefix(),
                                     getBaseUnitString(),
                                     getBaseUnitDefinition(),
                                     (float)exponent,
                                     isQuoted(),
                                     unitWasGuessed);
    }

    @Override boolean hasPermittedPrefix(UnitRepresentation r) {
        return getPrefix() == 0 || r.mayHaveSIPrefixes();
    }

    @Override public boolean wasGuessed() {
        return unitWasGuessed;
    }

    /**
     * Convert SI prefixes to the decimal power they correspond to
     * @param pfx the prefix string to be parsed
     * @return a parsed prefix object
     */
    static PrefixSplit splitUnitString(String pfx) {
        final Matcher m = unitWithPrefix.matcher(pfx);
        if (m.matches()) {
            char prefixLetter = m.group(1).equals("da")
                    ? UnitVal.CHAR_DECA
                    : m.group(1).charAt(0);
            int prefixPower;
            switch (prefixLetter) {
              case 'q': prefixPower = -30; break;
              case 'r': prefixPower = -27; break;
              case 'y': prefixPower = -24; break;
              case 'z': prefixPower = -21; break;
              case 'a': prefixPower = -18; break;
              case 'f': prefixPower = -15; break;
              case 'p': prefixPower = -12; break;
              case 'n': prefixPower =  -9; break;
              case 'u': prefixPower =  -6; break;
              case 'm': prefixPower =  -3; break;
              case 'c': prefixPower =  -2; break;
              case 'd': prefixPower =  -1; break;
              case UnitVal.CHAR_DECA:
                prefixPower = 1;
                break;
              case 'h': prefixPower =   2; break;
              case 'k': prefixPower =   3; break;
              case 'M': prefixPower =   6; break;
              case 'G': prefixPower =   9; break;
              case 'T': prefixPower =  12; break;
              case 'P': prefixPower =  15; break;
              case 'E': prefixPower =  18; break;
              case 'Z': prefixPower =  21; break;
              case 'Y': prefixPower =  24; break;
              case 'R': prefixPower =  27; break;
              case 'Q': prefixPower =  30; break;
              default:
                // The regexp should make this impossible
                throw new AssertionError("Impossible decimal prefix: '" + prefixLetter + "'");
            }

            final int pp = prefixPower;
            return new SimpleUnit.PrefixSplit() {
                public int getPower() {
                    return pp;
                }
                public boolean isBinaryPrefix() {
                    return false;
                }
                public String getUnit() {
                    return m.group(2);
                }
            };
        } else {
            return null;
        }
    }

    String prefixPowerToString() {
        String rval;
        switch (getPrefix()) {
          case -30: rval = "q"; break;
          case -27: rval = "r"; break;
          case -24: rval = "y"; break;
          case -21: rval = "z"; break;
          case -18: rval = "a"; break;
          case -15: rval = "f"; break;
          case -12: rval = "p"; break;
          case  -9: rval = "n"; break;
          case  -6: rval = "u"; break;
          case  -3: rval = "m"; break;
          case  -2: rval = "c"; break;
          case  -1: rval = "d"; break;
          case   0: rval = "";  break;
          case   1: rval = "da";break;
          case   2: rval = "h"; break;
          case   3: rval = "k"; break;
          case   6: rval = "M"; break;
          case   9: rval = "G"; break;
          case  12: rval = "T"; break;
          case  15: rval = "P"; break;
          case  18: rval = "E"; break;
          case  21: rval = "Z"; break;
          case  24: rval = "Y"; break;
          case  27: rval = "R"; break;
          case  30: rval = "Q"; break;
          default:
            // This should be impossible, since it's only
            // prefixLetterToPower which sets these values
            throw new AssertionError("Impossible decimal prefix power: " + getPrefix());
        }
        return rval;
    }

    String prefixPowerToStringLaTeX() {
        String rval;
        switch (getPrefix()) {
            // the Q, R, q, r prefixes require siunitx of at least 3.2.0 (or maybe 3.1.11)
          case -30: rval = "\\quecto"; break;
          case -27: rval = "\\ronto"; break;
          case -24: rval = "\\yocto"; break;
          case -21: rval = "\\zepto"; break;
          case -18: rval = "\\atto";  break;
          case -15: rval = "\\femto"; break;
          case -12: rval = "\\pico";  break;
          case  -9: rval = "\\nano";  break;
          case  -6: rval = "\\micro"; break;
          case  -3: rval = "\\milli"; break;
          case  -2: rval = "\\centi"; break;
          case  -1: rval = "\\deci";  break;
          case   0: rval = "";        break;
          case   1: rval = "\\deca";  break;
          case   2: rval = "\\hecto"; break;
          case   3: rval = "\\kilo";  break;
          case   6: rval = "\\mega";  break;
          case   9: rval = "\\giga";  break;
          case  12: rval = "\\tera";  break;
          case  15: rval = "\\peta";  break;
          case  18: rval = "\\exa";   break;
          case  21: rval = "\\zetta"; break;
          case  24: rval = "\\yotta"; break;
          case  27: rval = "\\ronna"; break;
          case  30: rval = "\\quetta"; break;
          default:
            // This should be impossible, since it's only
            // prefixLetterToPower which sets these values
            throw new AssertionError("Impossible decimal prefix power: " + getPrefix());
        }
        return rval;
    }
}
