/*
 * Copyright (C) 2014 Eric Butler
 *
 * 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.tapchatapp.android.app.ui;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;

import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import com.tapchatapp.android.app.event.BufferAddedEvent;
import com.tapchatapp.android.app.event.BufferChangedEvent;
import com.tapchatapp.android.app.event.BufferRemovedEvent;
import com.tapchatapp.android.app.event.ServiceStateChangedEvent;
import com.tapchatapp.android.app.fragment.BufferFragment;
import com.tapchatapp.android.app.TapchatApp;
import com.tapchatapp.android.client.TapchatService;
import com.tapchatapp.android.client.model.Buffer;
import com.tapchatapp.android.client.model.Connection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import javax.inject.Inject;

public class BuffersPagerAdapter extends TapchatFragmentStatePagerAdapter {

    private static final String KEY_BUFFERS = "com.tapchatapp.android.buffers";

    private final long mConnectionId;

    private final BuffersToDisplay mDisplay;
    private final Object mLock = new Object();

    private ArrayList<BufferInfo> mBuffers = new ArrayList<>();

    private int mConnectionState;

    @Inject Bus mBus;

    public BuffersPagerAdapter(Activity activity, long connectionId, BuffersToDisplay display) {
        super(activity.getFragmentManager());
        TapchatApp.get().inject(this);

        mConnectionId = connectionId;
        mDisplay = display;
    }

    public void registerBus() {
        mBus.register(this);
    }

    public void unregisterBus() {
        mBus.unregister(this);
    }

    @Override public Parcelable saveState() {
        Bundle bundle = (Bundle) super.saveState();
        if (bundle == null) {
            bundle = new Bundle();
        }
        bundle.putParcelableArrayList(KEY_BUFFERS, mBuffers);
        return bundle;
    }

    @Override public void restoreState(Parcelable state, ClassLoader loader) {
        super.restoreState(state, loader);
        if (state != null && state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            mBuffers = bundle.getParcelableArrayList(KEY_BUFFERS);
            notifyDataSetChanged();
        }
    }

    @Subscribe public void onServiceStateChanged(ServiceStateChangedEvent event) {
        TapchatService service = event.getService();

        mConnectionState = service.getConnectionState();
        if (mConnectionState != TapchatService.STATE_LOADED) {
            return;
        }

        Connection connection = service.getConnection(mConnectionId);

        final ArrayList<BufferInfo> buffers = new ArrayList<>();

        // Add new buffers.
        if (mDisplay == BuffersToDisplay.ConsoleOnly) {
            buffers.add(BufferInfo.forBuffer(connection.getConsoleBuffer()));
        } else {
            for (Buffer buffer : connection.getBuffers()) {
                if ((!buffer.isArchived()) || mDisplay == BuffersToDisplay.ShowArchived) {
                    buffers.add(BufferInfo.forBuffer(buffer));
                }
            }
        }

        Collections.sort(buffers, BufferInfo.COMPARATOR);
        mBuffers = buffers;
        notifyDataSetChanged();
    }

    @Subscribe public void onBufferAdded(final BufferAddedEvent event) {
        if (event.getConnection().getId() != mConnectionId) {
            return;
        }

        if (mConnectionState != TapchatService.STATE_LOADED) {
            return;
        }

        Buffer buffer = event.getBuffer();

        synchronized (mLock) {
            if (findBuffer(buffer.getId()) != null) {
                return;
            }

            final ArrayList<BufferInfo> buffers = new ArrayList<>(mBuffers);
            buffers.add(BufferInfo.forBuffer(buffer));
            Collections.sort(buffers, BufferInfo.COMPARATOR);

            mBuffers = buffers;
            notifyDataSetChanged();
        }
    }

    @Subscribe public void onBufferChanged(final BufferChangedEvent event) {
        if (event.getConnection().getId() != mConnectionId) {
            return;
        }

        if (mConnectionState != TapchatService.STATE_LOADED) {
            return;
        }

        synchronized (mLock) {
            Buffer buffer = event.getBuffer();
            BufferInfo info = findBuffer(buffer.getId());
            if (info != null) {
                String newName = buffer.getDisplayName();
                int newWeight = buffer.getWeight();

                if (buffer.isArchived() && mDisplay == BuffersToDisplay.Normal) {
                    mBuffers.remove(info);
                    notifyDataSetChanged();
                    return;
                }

                if ((!TextUtils.equals(info.mName, newName)) || info.mWeight != newWeight) {
                    info.mName = newName;
                    info.mWeight = newWeight;
                    Collections.sort(mBuffers, BufferInfo.COMPARATOR);
                    notifyDataSetChanged();
                }
            } else {
                if ((!buffer.isArchived()) || buffer.isArchived() && mDisplay == BuffersToDisplay.ShowArchived) {
                    final ArrayList<BufferInfo> buffers = new ArrayList<>(mBuffers);
                    buffers.add(BufferInfo.forBuffer(buffer));
                    Collections.sort(buffers, BufferInfo.COMPARATOR);
                    mBuffers = buffers;
                    notifyDataSetChanged();
                }
            }
        }
    }

    @Subscribe public void onBufferRemoved(final BufferRemovedEvent event) {
        if (event.getConnection().getId() != mConnectionId) {
            return;
        }

        if (mConnectionState != TapchatService.STATE_LOADED) {
            return;
        }

        Buffer buffer = event.getBuffer();
        synchronized (mLock) {
            for (int i = 0; i < mBuffers.size(); i++) {
                BufferInfo info = mBuffers.get(i);
                if (info.getBufferId() == buffer.getId()) {
                    mBuffers.remove(i);
                    notifyDataSetChanged();
                    return;
                }
            }
        }
    }

    @Override
    public int getCount() {
        return mBuffers.size();
    }

    @Override public String getPageTitle(int position) {
        return mBuffers.get(position).mName.toUpperCase();
    }

    @Override public Fragment getItem(int position) {
        BufferInfo info = mBuffers.get(position);
        return BufferFragment.create(info.getType(), mConnectionId, info.getBufferId());
    }

    @Override public int getItemPosition(Object object) {
        BufferFragment fragment = (BufferFragment) object;
        int index = findBufferIndex(fragment.getBufferId());
        if (index >= 0) {
            return index;
        } else {
            return POSITION_NONE;
        }
    }

    @Override public String getTag(int position) {
        return String.valueOf(mBuffers.get(position).getBufferId());
    }

    public BufferInfo getBufferInfo(int position) {
        return mBuffers.get(position);
    }

    public int getBufferIndex(long bufferId) {
        for (int i = 0; i < mBuffers.size(); i++) {
            BufferInfo info = mBuffers.get(i);
            if (info.getBufferId() == bufferId) {
                return i;
            }
        }
        return -1;
    }

    private BufferInfo findBuffer(long bufferId) {
        synchronized (mLock) {
            for (BufferInfo info : mBuffers) {
                if (info.getBufferId() == bufferId) {
                    return info;
                }
            }
            return null;
        }
    }

    public int findBufferIndex(long bufferId) {
        synchronized (mLock) {
            for (int i = 0; i < mBuffers.size(); i++) {
                BufferInfo info = mBuffers.get(i);
                if (info.getBufferId() == bufferId) {
                    return i;
                }
            }
            return -1;
        }
    }

    public enum BuffersToDisplay {
        Normal,
        ShowArchived,
        ConsoleOnly;

        public static BuffersToDisplay parseString(String name) {
            if (name == null) {
                return Normal;
            }
            switch (name) {
                case "archived":
                    return ShowArchived;
                case "console":
                    return ConsoleOnly;
                default:
                    return Normal;
            }
        }
    }

    public static final class BufferInfo implements Parcelable {

        private final long mConnectionId;
        private final long mBufferId;
        private final int mType;

        private int mWeight;
        private String mName;

        public static final Comparator<? super BufferInfo> COMPARATOR = new Comparator<BufferInfo>() {
            @Override public int compare(BufferInfo buffer, BufferInfo buffer1) {
                Integer weight1 = buffer.getWeight();
                Integer weight2 = buffer1.getWeight();
                if (weight1.equals(weight2)) {
                    return buffer.getName().compareToIgnoreCase(buffer1.getName());
                } else {
                    return weight2.compareTo(weight1);
                }
            }
        };

        public static final Creator<BufferInfo> CREATOR = new Creator<BufferInfo>() {
            @Override public BufferInfo createFromParcel(Parcel source) {
                return new BufferInfo(source.readLong(), source.readLong(), source.readInt(), source.readInt(),
                        source.readString());
            }

            @Override public BufferInfo[] newArray(int size) {
                return new BufferInfo[size];
            }
        };

        public BufferInfo(long cid, long bid, int type, int weight, String name) {
            mConnectionId = cid;
            mBufferId = bid;
            mType = type;
            mWeight = weight;
            mName = name;
        }

        public static BufferInfo forBuffer(Buffer buffer) {
            return new BufferInfo(buffer.getConnection().getId(), buffer.getId(), buffer.getType(), buffer.getWeight(),
                    buffer.getDisplayName());
        }

        public long getConnectionId() {
            return mConnectionId;
        }

        public long getBufferId() {
            return mBufferId;
        }

        public int getType() {
            return mType;
        }

        public String getName() {
            return mName;
        }

        public int getWeight() {
            return mWeight;
        }

        @Override public void writeToParcel(Parcel dest, int flags) {
            dest.writeLong(mConnectionId);
            dest.writeLong(mBufferId);
            dest.writeInt(mType);
            dest.writeInt(mWeight);
            dest.writeString(mName);
        }

        @Override public int describeContents() {
            return 0;
        }
    }
}