content: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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 org.apache.weex.thirdParty.zxing;
  
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.OutputStreamWriter;
  import java.nio.charset.Charset;
  import java.text.DateFormat;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.List;
  
  import android.app.Activity;
  import android.content.ContentValues;
  import android.content.SharedPreferences;
  import android.database.Cursor;
  import android.database.sqlite.SQLiteDatabase;
  import android.database.sqlite.SQLiteException;
  import android.database.sqlite.SQLiteOpenHelper;
  import android.net.Uri;
  import android.os.Environment;
  import android.preference.PreferenceManager;
  import android.util.Log;
  
  import com.google.zxing.BarcodeFormat;
  import com.google.zxing.Result;
  
  /**
   * <p>Manages functionality related to scan history.</p>
   *
   * @author Sean Owen
   */
  public final class HistoryManager {
  
    private static final String TAG = HistoryManager.class.getSimpleName();
  
    private static final int MAX_ITEMS = 2000;
  
    private static final String[] COLUMNS = {
        DBHelper.TEXT_COL,
        DBHelper.DISPLAY_COL,
        DBHelper.FORMAT_COL,
        DBHelper.TIMESTAMP_COL,
        DBHelper.DETAILS_COL,
    };
  
    private static final String[] COUNT_COLUMN = { "COUNT(1)" };
  
    private static final String[] ID_COL_PROJECTION = { DBHelper.ID_COL };
    private static final String[] ID_DETAIL_COL_PROJECTION = { DBHelper.ID_COL, DBHelper.DETAILS_COL };
  
    private final Activity activity;
    private final boolean enableHistory;
    public final String ITEM_NUMBER = "ITEM_NUMBER";
    public static final String SAVE_HISTORY = "SAVE_HISTORY";
    public static final String KEY_REMEMBER_DUPLICATES = "preferences_remember_duplicates";
    public static final String KEY_ENABLE_HISTORY = "preferences_history";
  
  
    public HistoryManager(Activity activity) {
      this.activity = activity;
      SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
      enableHistory = prefs.getBoolean(KEY_ENABLE_HISTORY, true);
    }
  
    public boolean hasHistoryItems() {
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      Cursor cursor = null;
      try {
        db = helper.getReadableDatabase();
        cursor = db.query(DBHelper.TABLE_NAME, COUNT_COLUMN, null, null, null, null, null);
        cursor.moveToFirst();
        return cursor.getInt(0) > 0;
      } finally {
        close(cursor, db);
      }
    }
  
    public List<HistoryItem> buildHistoryItems() {
      SQLiteOpenHelper helper = new DBHelper(activity);
      List<HistoryItem> items = new ArrayList<>();
      SQLiteDatabase db = null;
      Cursor cursor = null;
      try {
        db = helper.getReadableDatabase();
        cursor = db.query(DBHelper.TABLE_NAME, COLUMNS, null, null, null, null, DBHelper.TIMESTAMP_COL + " DESC");
        while (cursor.moveToNext()) {
          String text = cursor.getString(0);
          String display = cursor.getString(1);
          String format = cursor.getString(2);
          long timestamp = cursor.getLong(3);
          String details = cursor.getString(4);
          Result result = new Result(text, null, null, BarcodeFormat.valueOf(format), timestamp);
          items.add(new HistoryItem(result, display, details));
        }
      } finally {
        close(cursor, db);
      }
      return items;
    }
  
    public HistoryItem buildHistoryItem(int number) {
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      Cursor cursor = null;
      try {
        db = helper.getReadableDatabase();
        cursor = db.query(DBHelper.TABLE_NAME, COLUMNS, null, null, null, null, DBHelper.TIMESTAMP_COL + " DESC");
        cursor.move(number + 1);
        String text = cursor.getString(0);
        String display = cursor.getString(1);
        String format = cursor.getString(2);
        long timestamp = cursor.getLong(3);
        String details = cursor.getString(4);
        Result result = new Result(text, null, null, BarcodeFormat.valueOf(format), timestamp);
        return new HistoryItem(result, display, details);
      } finally {
        close(cursor, db);
      }
    }
    
    public void deleteHistoryItem(int number) {
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      Cursor cursor = null;
      try {
        db = helper.getWritableDatabase();      
        cursor = db.query(DBHelper.TABLE_NAME,
                          ID_COL_PROJECTION,
                          null, null, null, null,
                          DBHelper.TIMESTAMP_COL + " DESC");
        cursor.move(number + 1);
        db.delete(DBHelper.TABLE_NAME, DBHelper.ID_COL + '=' + cursor.getString(0), null);
      } finally {
        close(cursor, db);
      }
    }
  
    public void addHistoryItem(Result result) {
      // Do not save this item to the history if the preference is turned off, or the contents are
      // considered secure.
      if (!activity.getIntent().getBooleanExtra(SAVE_HISTORY, true) || !enableHistory) {
        return;
      }
  
      SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
      if (!prefs.getBoolean(KEY_REMEMBER_DUPLICATES, false)) {
        deletePrevious(result.getText());
      }
  
      ContentValues values = new ContentValues();
      values.put(DBHelper.TEXT_COL, result.getText());
      values.put(DBHelper.FORMAT_COL, result.getBarcodeFormat().toString());
      values.put(DBHelper.DISPLAY_COL, result.getText());
      values.put(DBHelper.TIMESTAMP_COL, System.currentTimeMillis());
  
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      try {
        db = helper.getWritableDatabase();      
        // Insert the new entry into the DB.
        db.insert(DBHelper.TABLE_NAME, DBHelper.TIMESTAMP_COL, values);
      } finally {
        close(null, db);
      }
    }
  
    public void addHistoryItemDetails(String itemID, String itemDetails) {
      // As we're going to do an update only we don't need need to worry
      // about the preferences; if the item wasn't saved it won't be udpated
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;    
      Cursor cursor = null;
      try {
        db = helper.getWritableDatabase();
        cursor = db.query(DBHelper.TABLE_NAME,
                          ID_DETAIL_COL_PROJECTION,
                          DBHelper.TEXT_COL + "=?",
                          new String[] { itemID },
                          null,
                          null,
                          DBHelper.TIMESTAMP_COL + " DESC",
                          "1");
        String oldID = null;
        String oldDetails = null;
        if (cursor.moveToNext()) {
          oldID = cursor.getString(0);
          oldDetails = cursor.getString(1);
        }
  
        if (oldID != null) {
          String newDetails;
          if (oldDetails == null) {
            newDetails = itemDetails;
          } else if (oldDetails.contains(itemDetails)) {
            newDetails = null;
          } else {
            newDetails = oldDetails + " : " + itemDetails;
          } 
          if (newDetails != null) {
            ContentValues values = new ContentValues();
            values.put(DBHelper.DETAILS_COL, newDetails);
            db.update(DBHelper.TABLE_NAME, values, DBHelper.ID_COL + "=?", new String[] { oldID });
          }
        }
  
      } finally {
        close(cursor, db);
      }
    }
  
    private void deletePrevious(String text) {
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      try {
        db = helper.getWritableDatabase();      
        db.delete(DBHelper.TABLE_NAME, DBHelper.TEXT_COL + "=?", new String[] { text });
      } finally {
        close(null, db);
      }
    }
  
    public void trimHistory() {
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      Cursor cursor = null;
      try {
        db = helper.getWritableDatabase();      
        cursor = db.query(DBHelper.TABLE_NAME,
                          ID_COL_PROJECTION,
                          null, null, null, null,
                          DBHelper.TIMESTAMP_COL + " DESC");
        cursor.move(MAX_ITEMS);
        while (cursor.moveToNext()) {
          String id = cursor.getString(0);
          Log.i(TAG, "Deleting scan history ID " + id);
          db.delete(DBHelper.TABLE_NAME, DBHelper.ID_COL + '=' + id, null);
        }
      } catch (SQLiteException sqle) {
        // We're seeing an error here when called in CaptureActivity.onCreate() in rare cases
        // and don't understand it. First theory is that it's transient so can be safely ignored.
        Log.w(TAG, sqle);
        // continue
      } finally {
        close(cursor, db);
      }
    }
  
    /**
     * <p>Builds a text representation of the scanning history. Each scan is encoded on one
     * line, terminated by a line break (\r\n). The values in each line are comma-separated,
     * and double-quoted. Double-quotes within values are escaped with a sequence of two
     * double-quotes. The fields output are:</p>
     *
     * <ol>
     *  <li>Raw text</li>
     *  <li>Display text</li>
     *  <li>Format (e.g. QR_CODE)</li>
     *  <li>Unix timestamp (milliseconds since the epoch)</li>
     *  <li>Formatted version of timestamp</li>
     *  <li>Supplemental info (e.g. price info for a product barcode)</li>
     * </ol>
     */
    CharSequence buildHistory() {
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      Cursor cursor = null;
      try {
        db = helper.getWritableDatabase();
        cursor = db.query(DBHelper.TABLE_NAME,
                          COLUMNS,
                          null, null, null, null,
                          DBHelper.TIMESTAMP_COL + " DESC");
  
        DateFormat format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
        StringBuilder historyText = new StringBuilder(1000);
        while (cursor.moveToNext()) {
  
          historyText.append('"').append(massageHistoryField(cursor.getString(0))).append("\",");
          historyText.append('"').append(massageHistoryField(cursor.getString(1))).append("\",");
          historyText.append('"').append(massageHistoryField(cursor.getString(2))).append("\",");
          historyText.append('"').append(massageHistoryField(cursor.getString(3))).append("\",");
  
          // Add timestamp again, formatted
          long timestamp = cursor.getLong(3);
          historyText.append('"').append(massageHistoryField(
              format.format(new Date(timestamp)))).append("\",");
  
          // Above we're preserving the old ordering of columns which had formatted data in position 5
  
          historyText.append('"').append(massageHistoryField(cursor.getString(4))).append("\"\r\n");
        }
        return historyText;
      } finally {
        close(cursor, db);
      }
    }
    
    void clearHistory() {
      SQLiteOpenHelper helper = new DBHelper(activity);
      SQLiteDatabase db = null;
      try {
        db = helper.getWritableDatabase();      
        db.delete(DBHelper.TABLE_NAME, null, null);
      } finally {
        close(null, db);
      }
    }
  
    static Uri saveHistory(String history) {
      File bsRoot = new File(Environment.getExternalStorageDirectory(), "BarcodeScanner");
      File historyRoot = new File(bsRoot, "History");
      if (!historyRoot.exists() && !historyRoot.mkdirs()) {
        Log.w(TAG, "Couldn't make dir " + historyRoot);
        return null;
      }
      File historyFile = new File(historyRoot, "history-" + System.currentTimeMillis() + ".csv");
      OutputStreamWriter out = null;
      try {
        out = new OutputStreamWriter(new FileOutputStream(historyFile), Charset.forName("UTF-8"));
        out.write(history);
        return Uri.parse("file://" + historyFile.getAbsolutePath());
      } catch (IOException ioe) {
        Log.w(TAG, "Couldn't access file " + historyFile + " due to " + ioe);
        return null;
      } finally {
        if (out != null) {
          try {
            out.close();
          } catch (IOException ioe) {
            // do nothing
          }
        }
      }
    }
  
    private static String massageHistoryField(String value) {
      return value == null ? "" : value.replace("\"","\"\"");
    }
    
    private static void close(Cursor cursor, SQLiteDatabase database) {
      if (cursor != null) {
        cursor.close();
      }
      if (database != null) {
        database.close();
      }
    }
  
  }
  