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 1024.
 * This will have been detected through the presence of one of the
 * 'binary' prefixes kibi, mebi, and so on, as specified by ISO/IEC
 * 80000-13 Sect.4 (or the earlier IEEE standard 1541-2002).
 *
 * Note that
 * <a href='https://www.bipm.org/en/cgpm-2022/resolution-3'>Resolution 3 of the 27th CGPM</a>
 * defines ronna/ronto/quetta/quecto, but doesn't mention the binary prefixes.
 */
public class SimpleBinaryUnit extends SimpleUnit {
    /**
     * A regexp to match a unit with an SI binary prefix.
     * This regexp requires there to be a unit: "Ki"
     * is not parsed as a prefix plus an empty unit.
     */
    private static final Pattern unitWithPrefix
            = Pattern.compile("([KMGTPEZY])i(.+)");

    SimpleBinaryUnit(UnitDefinitionMap.Resolver r,
                     int prefixPower,
                     String us,
                     UnitDefinition ud,
                     float e) {
        // quoted units (unknown by hypothesis) can't take binary
        // prefixes, so there's no need to support setting this flag
        // in the constructor
        super(r, prefixPower, us, ud, e, false);
    }

    @Override SimpleBinaryUnit reciprocate() {
        assert(! isQuoted());
        return new SimpleBinaryUnit(getUnitResolver(),
                                    getPrefix(),
                                    getBaseUnitString(),
                                    getBaseUnitDefinition(),
                                    -getExponent());
    }

    @Override SimpleBinaryUnit pow(double exponent) {
        assert(! isQuoted());
        return new SimpleBinaryUnit(getUnitResolver(),
                                    getPrefix(),
                                    getBaseUnitString(),
                                    getBaseUnitDefinition(),
                                    (float)exponent);
    }

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

    /**
     * Convert ISO/IEC 80000-13 binary prefixes to the binary 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).charAt(0);
            int prefixPower;
            switch (prefixLetter) {
              case 'K': prefixPower = 10; break;
              case 'M': prefixPower = 20; break;
              case 'G': prefixPower = 30; break;
              case 'T': prefixPower = 40; break;
              case 'P': prefixPower = 50; break;
              case 'E': prefixPower = 60; break;
              case 'Z': prefixPower = 70; break;
              case 'Y': prefixPower = 80; break;
              default:
                // the regexp should make this impossible
                throw new AssertionError("Impossible binary prefix: '" + prefixLetter + "i'");
            }

            final int pp = prefixPower;
            String possibleBaseUnit = m.group(2);

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

    String prefixPowerToString() {
        String rval;
        switch (getPrefix()) {
          case 10: rval = "Ki"; break;
          case 20: rval = "Mi"; break;
          case 30: rval = "Gi"; break;
          case 40: rval = "Ti"; break;
          case 50: rval = "Pi"; break;
          case 60: rval = "Ei"; break;
          case 70: rval = "Zi"; break;
          case 80: rval = "Yi"; 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()) {
          case 10: rval = "\\kibi"; break;
          case 20: rval = "\\mebi"; break;
          case 30: rval = "\\gibi"; break;
          case 40: rval = "\\tebi"; break;
          case 50: rval = "\\pebi"; break;
          case 60: rval = "\\exbi"; break;
          case 70: rval = "\\zebi"; break;
          case 80: rval = "\\yobi"; break;
          default:
            // This should be impossible, since it's only
            // prefixLetterToPower which sets these values
            throw new AssertionError("Impossible binary prefix power: " + getPrefix());
        }
        return rval;
    }
}
