package com.humbughq.mobile;

import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.Callable;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import android.util.Log;

import com.j256.ormlite.dao.RuntimeExceptionDao;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.misc.BaseDaoEnabled;
import com.j256.ormlite.misc.TransactionManager;
import com.j256.ormlite.stmt.Where;
import com.j256.ormlite.table.DatabaseTable;

/**
 * Ranges of messages we have received.
 * 
 */
@DatabaseTable(tableName = "ranges")
public class MessageRange extends BaseDaoEnabled<MessageRange, Integer> {
    @DatabaseField(generatedId = true)
    private int id;
    @DatabaseField()
    public int low;
    @DatabaseField()
    public int high;

    public MessageRange() {

    }

    public MessageRange(int low, int high) {
        this.low = low;
        this.high = high;
    }

    public static MessageRange getRangeContaining(int value,
            RuntimeExceptionDao<MessageRange, Integer> messageRangeDao) {
        List<MessageRange> ranges;
        try {
            ranges = messageRangeDao.queryBuilder().where().le("low", value)
                    .and().ge("high", value).query();
            if (ranges.size() == 1) {
                return ranges.get(0);
            } else if (ranges.size() != 0) {
                Log.wtf("rangecheck",
                        "Expected one range, got " + ranges.size()
                                + " when looking for ID " + value);
            }
        } catch (SQLException e) {
            // This is nonfatal.
            ZLog.logException(e);
        }

        return null;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 31).append(low).append(high)
                .toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        MessageRange rhs = (MessageRange) obj;
        return new EqualsBuilder().append(this.low, rhs.low)
                .append(this.high, rhs.high).isEquals();
    }

    // / Update or create the final range for new messages from server events
    public static void updateNewMessagesRange(ZulipApp app, int maxId) {
        synchronized (app.updateRangeLock) {
            RuntimeExceptionDao<MessageRange, Integer> rangeDao = app
                    .getDao(MessageRange.class);

            MessageRange currentRange = MessageRange.getRangeContaining(
                    app.getMaxMessageId(), rangeDao);
            if (currentRange == null) {
                currentRange = new MessageRange(app.getMaxMessageId(),
                        app.getMaxMessageId());
            }

            if (currentRange.high <= maxId) {
                currentRange.high = maxId;
                rangeDao.createOrUpdate(currentRange);
            }
        }

        app.setMaxMessageId(maxId);
    }

    // Create a range for fetched messages, merging with other ranges if
    // necessary. Messages between low and high (both inclusive) must exist in
    // the DB.
    public static void markRange(ZulipApp app, final int low, final int high) {
        final RuntimeExceptionDao<MessageRange, Integer> messageRangeDao = app
                .getDao(MessageRange.class);
        try {
            synchronized (app.updateRangeLock) {
                TransactionManager.callInTransaction(app.getDatabaseHelper()
                        .getConnectionSource(), new Callable<Void>() {
                    public Void call() throws Exception {
                        MessageRange rng = new MessageRange(low, high);
                        Where<MessageRange, Integer> where = messageRangeDao
                                .queryBuilder().orderBy("low", true).where();
                        @SuppressWarnings("unchecked")
                        List<MessageRange> ranges = where.or(
                                where.and(where.ge("high", rng.low - 1),
                                        where.le("high", rng.high + 1)),
                                where.and(where.ge("low", rng.low - 1),
                                        where.le("low", rng.high + 1))).query();

                        if (ranges.size() == 0) {
                            // Nothing to consolidate
                            messageRangeDao.createOrUpdate(rng);
                            return null;
                        }
                        Log.i("", "our low: " + rng.low + ", our high: "
                                + rng.high);
                        int db_low = ranges.get(0).low;
                        int db_high = ranges.get(ranges.size() - 1).high;
                        Log.i("", "their low: " + db_low + ", their high: "
                                + db_high);
                        if (db_low < rng.low) {
                            rng.low = db_low;
                        }
                        if (db_high > rng.high) {
                            rng.high = db_high;
                        }
                        messageRangeDao.delete(ranges);
                        messageRangeDao.createOrUpdate(rng);

                        return null;
                    }
                });
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

@@ -3782,80 +3782,8 @@
 n %7B%0A
-                        MessageRange rng = new MessageRange(low, high);%0A
     
@@ -4109,20 +4109,16 @@
 %22high%22, 
-rng.
 low - 1)
@@ -4176,20 +4176,16 @@
 %22high%22, 
-rng.
 high + 1
@@ -4246,20 +4246,16 @@
 (%22low%22, 
-rng.
 low - 1)
@@ -4312,20 +4312,16 @@
 (%22low%22, 
-rng.
 high + 1
@@ -4362,220 +4362,109 @@
     
-if (ranges.size() == 0) %7B%0A                            // Nothing to consolidate%0A                            messageRangeDao.createOrUpdate(rng);%0A                            return null;%0A                    
+MessageRange rng = new MessageRange(low, high);%0A                        if (ranges.size() %3E 0) %7B%0A
     
-%7D%0A
     
@@ -4564,16 +4564,20 @@
         
+    
 + rng.hi
@@ -4601,24 +4601,28 @@
             
+ 
+   
 int db_low =
@@ -4637,24 +4637,28 @@
 get(0).low;%0A
+    
             
@@ -4735,32 +4735,36 @@
                 
+  
+  
 Log.i(%22%22, %22their
@@ -4831,16 +4831,20 @@
         
+    
 + db_hig
@@ -4867,24 +4867,28 @@
             
+   
+ 
 if (db_low %3C
@@ -4923,24 +4923,28 @@
             
+    
 rng.low = db
@@ -4945,24 +4945,28 @@
 w = db_low;%0A
+    
             
@@ -4999,24 +4999,28 @@
             
+    
 if (db_high 
@@ -5025,32 +5025,36 @@
 h %3E rng.high) %7B%0A
+    
                 
@@ -5101,34 +5101,42 @@
                 
-%7D%0A
+    %7D%0A    
                 
@@ -5171,24 +5171,50 @@
 te(ranges);%0A
+                        %7D%0A
             
@@ -5262,17 +5262,16 @@
 e(rng);%0A
-%0A
         

Refactor markRange