content: /*
   * Copyright (C) 2013 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.timezonepicker;
  
  import android.content.Context;
  import android.text.Spannable;
  import android.text.Spannable.Factory;
  import android.text.format.DateUtils;
  import android.text.format.Time;
  import android.text.style.ForegroundColorSpan;
  import android.util.Log;
  import android.util.SparseArray;
  
  import java.lang.reflect.Field;
  import java.text.DateFormat;
  import java.util.Arrays;
  import java.util.Date;
  import java.util.Formatter;
  import java.util.Locale;
  import java.util.TimeZone;
  
  public class TimeZoneInfo implements Comparable<TimeZoneInfo> {
      private static final int GMT_TEXT_COLOR = TimeZonePickerUtils.GMT_TEXT_COLOR;
      private static final int DST_SYMBOL_COLOR = TimeZonePickerUtils.DST_SYMBOL_COLOR;
      private static final char SEPARATOR = ',';
      private static final String TAG = null;
      public static int NUM_OF_TRANSITIONS = 6;
      public static long time = System.currentTimeMillis() / 1000;
      public static boolean is24HourFormat;
      private static final Factory mSpannableFactory = Spannable.Factory.getInstance();
  
      TimeZone mTz;
      public String mTzId;
      int mRawoffset;
      public String mCountry;
      public int groupId;
      public String mDisplayName;
      private Time recycledTime = new Time();
      private static StringBuilder mSB = new StringBuilder(50);
      private static Formatter mFormatter = new Formatter(mSB, Locale.getDefault());
  
      public TimeZoneInfo(TimeZone tz, String country) {
          mTz = tz;
          mTzId = tz.getID();
          mCountry = country;
          mRawoffset = tz.getRawOffset();
      }
  
      SparseArray<String> mLocalTimeCache = new SparseArray<String>();
      long mLocalTimeCacheReferenceTime = 0;
      static private long mGmtDisplayNameUpdateTime;
      static private SparseArray<CharSequence> mGmtDisplayNameCache =
              new SparseArray<CharSequence>();
  
      public String getLocalTime(long referenceTime) {
          recycledTime.timezone = TimeZone.getDefault().getID();
          recycledTime.set(referenceTime);
  
          int currYearDay = recycledTime.year * 366 + recycledTime.yearDay;
  
          recycledTime.timezone = mTzId;
          recycledTime.set(referenceTime);
  
          String localTimeStr = null;
  
          int hourMinute = recycledTime.hour * 60 +
                  recycledTime.minute;
  
          if (mLocalTimeCacheReferenceTime != referenceTime) {
              mLocalTimeCacheReferenceTime = referenceTime;
              mLocalTimeCache.clear();
          } else {
              localTimeStr = mLocalTimeCache.get(hourMinute);
          }
  
          if (localTimeStr == null) {
              String format = "%I:%M %p";
              if (currYearDay != (recycledTime.year * 366 + recycledTime.yearDay)) {
                  if (is24HourFormat) {
                      format = "%b %d %H:%M";
                  } else {
                      format = "%b %d %I:%M %p";
                  }
              } else if (is24HourFormat) {
                  format = "%H:%M";
              }
  
              // format = "%Y-%m-%d %H:%M";
              localTimeStr = recycledTime.format(format);
              mLocalTimeCache.put(hourMinute, localTimeStr);
          }
  
          return localTimeStr;
      }
  
      public int getLocalHr(long referenceTime) {
          recycledTime.timezone = mTzId;
          recycledTime.set(referenceTime);
          return recycledTime.hour;
      }
  
      public int getNowOffsetMillis() {
          return mTz.getOffset(System.currentTimeMillis());
      }
  
      /*
       * The method is synchronized because there's one mSB, which is used by
       * mFormatter, per instance. If there are multiple callers for
       * getGmtDisplayName, the output may be mangled.
       */
      public synchronized CharSequence getGmtDisplayName(Context context) {
          // TODO Note: The local time is shown in current time (current GMT
          // offset) which may be different from the time specified by
          // mTimeMillis
  
          final long nowMinute = System.currentTimeMillis() / DateUtils.MINUTE_IN_MILLIS;
          final long now = nowMinute * DateUtils.MINUTE_IN_MILLIS;
          final int gmtOffset = mTz.getOffset(now);
          int cacheKey;
  
          boolean hasFutureDST = mTz.useDaylightTime();
          if (hasFutureDST) {
              cacheKey = (int) (gmtOffset + 36 * DateUtils.HOUR_IN_MILLIS);
          } else {
              cacheKey = (int) (gmtOffset - 36 * DateUtils.HOUR_IN_MILLIS);
          }
  
          CharSequence displayName = null;
          if (mGmtDisplayNameUpdateTime != nowMinute) {
              mGmtDisplayNameUpdateTime = nowMinute;
              mGmtDisplayNameCache.clear();
          } else {
              displayName = mGmtDisplayNameCache.get(cacheKey);
          }
  
          if (displayName == null) {
              mSB.setLength(0);
              int flags = DateUtils.FORMAT_ABBREV_ALL;
              flags |= DateUtils.FORMAT_SHOW_TIME;
              if (TimeZoneInfo.is24HourFormat) {
                  flags |= DateUtils.FORMAT_24HOUR;
              }
  
              // mFormatter writes to mSB
              DateUtils.formatDateRange(context, mFormatter, now, now, flags, mTzId);
              mSB.append("  ");
              int gmtStart = mSB.length();
              TimeZonePickerUtils.appendGmtOffset(mSB, gmtOffset);
              int gmtEnd = mSB.length();
  
              int symbolStart = 0;
              int symbolEnd = 0;
              if (hasFutureDST) {
                  mSB.append(' ');
                  symbolStart = mSB.length();
                  mSB.append(TimeZonePickerUtils.getDstSymbol()); // Sun symbol
                  symbolEnd = mSB.length();
              }
  
              // Set the gray colors.
              Spannable spannableText = mSpannableFactory.newSpannable(mSB);
              spannableText.setSpan(new ForegroundColorSpan(GMT_TEXT_COLOR),
                      gmtStart, gmtEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  
              if (hasFutureDST) {
                  spannableText.setSpan(new ForegroundColorSpan(DST_SYMBOL_COLOR),
                          symbolStart, symbolEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
              }
              displayName = spannableText;
              mGmtDisplayNameCache.put(cacheKey, displayName);
          }
          return displayName;
      }
  
      public boolean hasSameRules(TimeZoneInfo tzi) {
          return this.mTz.hasSameRules(tzi.mTz);
      }
  
      @Override
      public String toString() {
          StringBuilder sb = new StringBuilder();
  
          final String country = this.mCountry;
          final TimeZone tz = this.mTz;
  
          sb.append(mTzId);
          sb.append(SEPARATOR);
          sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.LONG));
          sb.append(SEPARATOR);
          sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.SHORT));
          sb.append(SEPARATOR);
          if (tz.useDaylightTime()) {
              sb.append(tz.getDisplayName(true, TimeZone.LONG));
              sb.append(SEPARATOR);
              sb.append(tz.getDisplayName(true, TimeZone.SHORT));
          } else {
              sb.append(SEPARATOR);
          }
          sb.append(SEPARATOR);
          sb.append(tz.getRawOffset() / 3600000f);
          sb.append(SEPARATOR);
          sb.append(tz.getDSTSavings() / 3600000f);
          sb.append(SEPARATOR);
          sb.append(country);
          sb.append(SEPARATOR);
  
          // 1-1-2013 noon GMT
          sb.append(getLocalTime(1357041600000L));
          sb.append(SEPARATOR);
  
          // 3-15-2013 noon GMT
          sb.append(getLocalTime(1363348800000L));
          sb.append(SEPARATOR);
  
          // 7-1-2013 noon GMT
          sb.append(getLocalTime(1372680000000L));
          sb.append(SEPARATOR);
  
          // 11-01-2013 noon GMT
          sb.append(getLocalTime(1383307200000L));
          sb.append(SEPARATOR);
  
          sb.append('\n');
          return sb.toString();
      }
  
      private static String formatTime(DateFormat df, int s) {
          long ms = s * 1000L;
          return df.format(new Date(ms));
      }
  
      /*
       * Returns a negative integer if this instance is less than the other; a
       * positive integer if this instance is greater than the other; 0 if this
       * instance has the same order as the other.
       */
      @Override
      public int compareTo(TimeZoneInfo other) {
          if (this.getNowOffsetMillis() != other.getNowOffsetMillis()) {
              return (other.getNowOffsetMillis() < this.getNowOffsetMillis()) ? -1 : 1;
          }
  
          // By country
          if (this.mCountry == null) {
              if (other.mCountry != null) {
                  return 1;
              }
          }
  
          if (other.mCountry == null) {
              return -1;
          } else {
              int diff = this.mCountry.compareTo(other.mCountry);
  
              if (diff != 0) {
                  return diff;
              }
          }
  
          // Finally diff by display name
          if (mDisplayName != null && other.mDisplayName != null)
              return this.mDisplayName.compareTo(other.mDisplayName);
  
          return this.mTz.getDisplayName(Locale.getDefault()).compareTo(
                  other.mTz.getDisplayName(Locale.getDefault()));
  
      }
  }
  