/*
 * Copyright (C) 2014 Lucien Loiseau
 * This file is part of Rumble.
 * Rumble is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Rumble is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with Rumble.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.disrupted.rumble.database;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import org.disrupted.rumble.database.events.ChatWipedEvent;
import org.disrupted.rumble.database.objects.ChatMessage;
import org.disrupted.rumble.database.objects.Contact;

import java.util.ArrayList;
import java.util.List;

import de.greenrobot.event.EventBus;

/**
 * @author Lucien Loiseau
 */
public class ChatMessageDatabase extends Database {

    private static final String TAG = "ChatMessageDatabase";

    public static final String TABLE_NAME        = "chat_message";
    public static final String ID               = "_id";
    public static final String UUID             = "uuid";
    public static final String AUTHOR_DBID      = "cdbid";       // author foreign key
    public static final String MESSAGE          = "message";     // the post itself
    public static final String FILE_NAME        = "filename";    // the name of the attached file
    public static final String TIME_OF_CREATION = "toc";         // time of creation
    public static final String TIME_OF_ARRIVAL  = "toa";         // time of arrival at current node
    public static final String PROTOCOL         = "protocol";    // the protocol from which we receive the message
    public static final String USERREAD         = "read";
    public static final String RECIPENTS        = "nb_recipients";

    public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
            " (" + ID          + " INTEGER PRIMARY KEY, "
                 + UUID        + " TEXT, "
                 + AUTHOR_DBID + " INTEGER, "
                 + MESSAGE     + " TEXT, "
                 + FILE_NAME   + " TEXT, "
                 + TIME_OF_CREATION  + " INTEGER, "
                 + TIME_OF_ARRIVAL   + " INTEGER, "
                 + USERREAD          + " INTEGER, "
                 + PROTOCOL          + " TEXT, "
                 + RECIPENTS         + " INTEGER, "
                 + " UNIQUE ( "+UUID+" ), "
                 + " FOREIGN KEY ( "+ AUTHOR_DBID + " ) REFERENCES " + ContactDatabase.TABLE_NAME + " ( " + ContactDatabase.ID   + " ) "
            + " );";

    public ChatMessageDatabase(Context context, SQLiteOpenHelper databaseHelper) {
        super(context, databaseHelper);
    }

    @Override
    public String getTableName() {
        return TABLE_NAME;
    }

    public static class ChatMessageQueryOption {
        public static final long FILTER_TOA_FROM   = 0x0001;
        public static final long FILTER_TOA_TO     = 0x0002;
        public static final long FILTER_READ       = 0x0004;

        public enum QUERY_RESULT {
            COUNT,
            LIST_OF_MESSAGE
        }

        public long         filterFlags;
        public long         to_toa;
        public long         from_toa;
        public int          answerLimit;
        public boolean      read;
        public QUERY_RESULT query_result;

        public ChatMessageQueryOption() {
            filterFlags = 0x00;
            to_toa = 0;
            from_toa = 0;
            answerLimit = 0;
            read = true;
            query_result = QUERY_RESULT.LIST_OF_MESSAGE;
        }
    }

    public boolean getChatMessage(final ChatMessageQueryOption options, DatabaseExecutor.ReadableQueryCallback callback){
        return DatabaseFactory.getDatabaseExecutor(context).addQuery(
                new DatabaseExecutor.ReadableQuery() {
                    @Override
                    public Object read() {
                        return getChatMessage(options);
                    }
                }, callback);
    }
    private Object getChatMessage(ChatMessageQueryOption options) {
        if(options == null)
            options = new ChatMessageQueryOption();


        /* 1st:  configure what the query will return */
        String select = " * ";
        switch (options.query_result) {
            case COUNT:
                select = " COUNT(*) ";
                break;
            case LIST_OF_MESSAGE:
                select = " cm.* ";
                break;
        }

        StringBuilder query = new StringBuilder(
                "SELECT "+select+" FROM "+ ChatMessageDatabase.TABLE_NAME+" cm " +
                " JOIN "+ContactDatabase.TABLE_NAME + " c" +
                " ON cm."+ChatMessageDatabase.AUTHOR_DBID+" = c."+ContactDatabase.ID
        );


        /* 2nd: we add the constraints */
        List<String> argumentList = new ArrayList<String>();
        boolean firstwhere = true;
        if(options.filterFlags > 0)
            query.append(" WHERE ( ");

        if ((options.filterFlags & ChatMessageQueryOption.FILTER_TOA_FROM) == ChatMessageQueryOption.FILTER_TOA_FROM) {
            firstwhere = false;
            query.append(" cm." + ChatMessageDatabase.TIME_OF_ARRIVAL + " > ? ");
            argumentList.add(Long.toString(options.from_toa));
        }
        if ((options.filterFlags & ChatMessageQueryOption.FILTER_TOA_TO) == ChatMessageQueryOption.FILTER_TOA_TO) {
            if(!firstwhere)
                query.append(" AND ");
            firstwhere = false;
            query.append(" cm." + ChatMessageDatabase.TIME_OF_ARRIVAL + " < ? ");
            argumentList.add(Long.toString(options.to_toa));
        }
        if((options.filterFlags & ChatMessageQueryOption.FILTER_READ) == ChatMessageQueryOption.FILTER_READ) {
            if(!firstwhere)
                query.append(" AND ");
            if(options.read)
                query.append(" cm." + ChatMessageDatabase.USERREAD + " =  1");
            else
                query.append(" cm." + ChatMessageDatabase.USERREAD + " =  0");
        }
        if(options.filterFlags > 0)
            query.append(" ) ");

        query.append(" ORDER BY " + ChatMessageDatabase.TIME_OF_ARRIVAL + " DESC");

        if(options.answerLimit > 0) {
            query.append(" LIMIT ? ");
            argumentList.add(Integer.toString(options.answerLimit));
        }

        /* perform the query
        Log.d(TAG, "[Q] query: " + query.toString());
        for(String argument : argumentList) {
            Log.d(TAG, argument+" ");
        }
        */
        SQLiteDatabase database = databaseHelper.getReadableDatabase();
        Cursor cursor = database.rawQuery(query.toString(),argumentList.toArray(new String[argumentList.size()]));
        if(cursor == null)
            return null;

        try {
            switch (options.query_result) {
                case COUNT:
                    cursor.moveToFirst();
                    return cursor.getInt(0);
                case LIST_OF_MESSAGE:
                    ArrayList<ChatMessage> listChatMessage = new ArrayList<ChatMessage>();
                    for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
                        listChatMessage.add(cursorToChatMessage(cursor));
                    }
                    return listChatMessage;
                default:
                    return null;
            }
        } finally {
            cursor.close();
       }
    }

    public ChatMessage getChatMessage(String uuid) {
        Cursor cursor = null;
        try {
            SQLiteDatabase database = databaseHelper.getReadableDatabase();
            cursor = database.query(TABLE_NAME, null, UUID+" = ?",new String[]{uuid}, null, null, null);
            if(cursor == null)
                return null;
            if(cursor.moveToFirst() && !cursor.isAfterLast())
                return cursorToChatMessage(cursor);
        } finally {
            if(cursor != null)
                cursor.close();
        }
        return null;
    }

    public long getChatMessageDBID(String uuid) {
        Cursor cursor = null;
        try {
            SQLiteDatabase database = databaseHelper.getReadableDatabase();
            cursor = database.query(TABLE_NAME, new String[] {ID}, UUID+" = ?",new String[]{uuid}, null, null, null);
            if(cursor == null)
                return -1;
            if(cursor.moveToFirst() && !cursor.isAfterLast())
                return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
            else
                return -1;
        } finally {
            if(cursor != null)
                cursor.close();
        }
    }

    public long insertMessage(ChatMessage chatMessage){
        ContentValues contentValues = new ContentValues();

        long contact_DBID = DatabaseFactory.getContactDatabase(context).getContactDBID(chatMessage.getAuthor().getUid());
        if(contact_DBID <0)
            return -1;

        contentValues.put(UUID,            chatMessage.getUUID());
        contentValues.put(AUTHOR_DBID,     contact_DBID);
        contentValues.put(MESSAGE,         chatMessage.getMessage());
        contentValues.put(FILE_NAME,       chatMessage.getAttachedFile());
        contentValues.put(TIME_OF_CREATION,chatMessage.getAuthorTimestamp());
        contentValues.put(TIME_OF_ARRIVAL, chatMessage.getTimestamp());
        contentValues.put(PROTOCOL,        chatMessage.getProtocolID());
        contentValues.put(USERREAD,        chatMessage.hasUserReadAlready() ? 1 : 0);
        contentValues.put(RECIPENTS,       chatMessage.getNbRecipients());

        return  databaseHelper.getWritableDatabase().insertWithOnConflict(TABLE_NAME, null, contentValues, SQLiteDatabase.CONFLICT_IGNORE);
    }

    public long updateMessage(ChatMessage chatMessage){
        ContentValues contentValues  = new ContentValues();
        contentValues.put(UUID,      chatMessage.getUUID());
        contentValues.put(USERREAD,  chatMessage.hasUserReadAlready() ? 1 : 0);
        contentValues.put(RECIPENTS, chatMessage.getNbRecipients());
        return databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, UUID + " = ? ", new String[]{chatMessage.getUUID()});
    }

    private ChatMessage cursorToChatMessage(final Cursor cursor) {
        if(cursor == null)
            return null;
        if(cursor.isAfterLast())
            return null;

        String  uuid        = cursor.getString(cursor.getColumnIndexOrThrow(UUID));
        long author_dbid = cursor.getLong(cursor.getColumnIndexOrThrow(AUTHOR_DBID));
        String message     = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE));
        String  filename    = cursor.getString(cursor.getColumnIndexOrThrow(FILE_NAME));
        long toc = cursor.getLong(cursor.getColumnIndexOrThrow(TIME_OF_CREATION));
        long    toa         = cursor.getLong(cursor.getColumnIndexOrThrow(TIME_OF_ARRIVAL));
        String  protocol    = cursor.getString(cursor.getColumnIndexOrThrow(PROTOCOL));
        boolean userRead    = (cursor.getInt(cursor.getColumnIndexOrThrow(USERREAD)) == 1);
        int     recipients  = cursor.getInt(cursor.getColumnIndexOrThrow(RECIPENTS));

        Contact author   = DatabaseFactory.getContactDatabase(context).getContact(author_dbid);
        ChatMessage chatMessage = new ChatMessage(author, message, toc, toa, protocol);
        chatMessage.setUserRead(userRead);
        chatMessage.setAttachedFile(filename);
        chatMessage.setUUID(uuid);
        chatMessage.setNbRecipients(recipients);

        return chatMessage;
    }

    public void wipe() {
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
        database.delete(TABLE_NAME, null, null);
        EventBus.getDefault().post(new ChatWipedEvent());
    }
}