001package net.filebot.format;
002
003import java.math.BigDecimal;
004import java.math.RoundingMode;
005import java.text.DecimalFormat;
006import java.text.DecimalFormatSymbols;
007import java.util.Locale;
008import java.util.stream.Stream;
009
010import net.filebot.util.AlphanumComparator;
011
012public class AutoScaleInteger implements StringBinding, Comparable<Object> {
013
014        public enum Scale {
015
016                AUTO(1), K(1_000), M(1_000_000), G(1_000_000_000);
017
018                public final int factor;
019
020                private Scale(int factor) {
021                        this.factor = factor;
022                }
023
024                public static Scale find(long value) {
025                        return Stream.of(G, M, K).filter(m -> value >= m.factor).findFirst().orElse(AUTO);
026                }
027        }
028
029        protected final long value;
030        protected final Scale scale;
031
032        public AutoScaleInteger(long value, Scale scale) {
033                this.value = value;
034                this.scale = scale;
035        }
036
037        public long getValue() {
038                return value;
039        }
040
041        public Scale getScale() {
042                return scale;
043        }
044
045        public AutoScaleInteger scale(Scale scale) {
046                return new AutoScaleInteger(value, scale);
047        }
048
049        public AutoScaleInteger getG() {
050                return scale(Scale.G);
051        }
052
053        public AutoScaleInteger getM() {
054                return scale(Scale.M);
055        }
056
057        public AutoScaleInteger getK() {
058                return scale(Scale.K);
059        }
060
061        public long toLong() {
062                return value / scale.factor;
063        }
064
065        public double toDouble() {
066                return (double) value / scale.factor;
067        }
068
069        public int toInteger() {
070                return (int) toLong();
071        }
072
073        public float toFloat() {
074                return (float) toDouble();
075        }
076
077        public BigDecimal toNumber() {
078                return BigDecimal.valueOf(toDouble());
079        }
080
081        @Override
082        public int compareTo(Object other) {
083                if (other instanceof Integer) {
084                        Number n = (Number) other;
085                        return Integer.compare(toInteger(), n.intValue());
086                }
087
088                if (other instanceof Number) {
089                        Number n = (Number) other;
090                        return Double.compare(toDouble(), n.doubleValue());
091                }
092
093                if (other instanceof AutoScaleInteger) {
094                        AutoScaleInteger n = (AutoScaleInteger) other;
095                        return Long.compare(value, n.value);
096                }
097
098                return AlphanumComparator.getInstance().compare(toString(), other.toString());
099        }
100
101        @Override
102        public boolean equals(Object other) {
103                return (other instanceof Number && compareTo(other) == 0) || (other instanceof CharSequence && toString().equals(other));
104        }
105
106        public Object asType(Class<?> c) {
107                if (c == int.class || c == Integer.class) {
108                        return toInteger();
109                }
110
111                if (c == long.class || c == Long.class) {
112                        return toLong();
113                }
114
115                if (c == float.class || c == Float.class) {
116                        return toFloat();
117                }
118
119                if (c == double.class || c == Double.class) {
120                        return toDouble();
121                }
122
123                if (c == Number.class) {
124                        return toNumber();
125                }
126
127                if (c == String.class) {
128                        return toString();
129                }
130
131                // fail with type cast exception message
132                return c.cast(this);
133        }
134
135        public Number div(Number divisor) {
136                return BigDecimal.valueOf(toDouble() / divisor.doubleValue());
137        }
138
139        public Number round(int precision) {
140                return BigDecimal.valueOf(toDouble()).setScale(precision, RoundingMode.HALF_UP);
141        }
142
143        public String format(String pattern) {
144                return format(pattern, Locale.ROOT);
145        }
146
147        public String format(String pattern, String locale) {
148                return format(pattern, Locale.forLanguageTag(locale));
149        }
150
151        public String format(String pattern, Locale locale) {
152                DecimalFormat f = new DecimalFormat(pattern, new DecimalFormatSymbols(locale));
153                return f.format(toDouble());
154        }
155
156        public String getUnit(Scale scale) {
157                return scale == Scale.AUTO ? null : scale.name();
158        }
159
160        @Override
161        public String toString() {
162                // auto scale or fixed scale
163                return scale == Scale.AUTO ? toString(Scale.find(value)) : toString(scale);
164        }
165
166        public String toString(Scale target) {
167                String u = getUnit(target);
168
169                // format absolute value
170                if (u == null) {
171                        return String.format(Locale.ROOT, "%,d", value);
172                }
173
174                // scaled value
175                double v = (double) value / target.factor;
176
177                // format single digit scaled values with one fraction digit
178                if (v < 9.5 && target.factor > 1) {
179                        return String.format(Locale.ROOT, "%,.1f %s", v, u);
180                }
181
182                // format scaled value
183                return String.format(Locale.ROOT, "%,.0f %s", v, u);
184        }
185
186}