/*-
 *  Copyright (C) 2009 Peter Baldwin   
 *  
 *  This program 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.
 *
 *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.peterbaldwin.vlcremote.fragment;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListAdapter;
import android.widget.Toast;
import java.net.HttpURLConnection;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.peterbaldwin.client.android.vlcremote.R;
import org.peterbaldwin.vlcremote.app.PickServerActivity;
import org.peterbaldwin.vlcremote.intent.Intents;
import org.peterbaldwin.vlcremote.model.Preferences;
import org.peterbaldwin.vlcremote.model.Server;
import org.peterbaldwin.vlcremote.preference.ProgressCategory;
import org.peterbaldwin.vlcremote.receiver.PhoneStateChangedReceiver;
import org.peterbaldwin.vlcremote.sweep.PortSweeper;
import org.peterbaldwin.vlcremote.widget.Buttons;

public final class PickServerFragment extends PreferenceFragment implements PortSweeper.Callback,
        ServerInfoDialog.ServerInfoDialogListener, OnPreferenceChangeListener {

    private static final String TAG = "PickServer";

    private static final String PACKAGE_NAME = R.class.getPackage().getName();
    
    private static final ComponentName PHONE_STATE_RECEIVER = new ComponentName(PACKAGE_NAME,
            PhoneStateChangedReceiver.class.getName());

    private static final String KEY_SCREEN_INTERFACE = "preference_screen_interface";
    private static final String KEY_SCREEN_PRESETS = "preference_screen_presets";
    private static final String KEY_SCREEN_BUTTONS = "preference_screen_buttons";
    private static final String KEY_SCREEN_NOTIFICATION = "preference_screen_notification";
    private static final String KEY_WIFI = "wifi";
    private static final String KEY_SERVERS = "servers";
    private static final String KEY_ADD_SERVER = "add_server";
    private static final String KEY_PAUSE_FOR_CALL = "pause_for_call";
    
    public static final String STATE_HOSTS = "hosts";

    private static final String DIALOG_ADD_SERVER = "add_server";
    private static final String DIALOG_EDIT_SERVER = "edit_server";

    private static final int CONTEXT_FORGET = Menu.FIRST;
    private static final int CONTEXT_EDIT_SERVER = 2;

    private BroadcastReceiver mReceiver;
    
    private int mPort;

    private Map<String, Server> mRemembered;

    private CheckBoxPreference mPreferenceWiFi;
    private ListPreference mPreferenceButtonFirst;
    private ListPreference mPreferenceButtonSecond;
    private ListPreference mPreferenceButtonThird;
    private ListPreference mPreferenceButtonFourth;
    private ListPreference mPreferenceButtonFifth;
    private ProgressCategory mProgressCategory;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((PickServerActivity) activity).setServerInfoDialogListener(this);
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.server_settings);
        
        PreferenceScreen preferenceScreen = getPreferenceScreen();

        mPreferenceWiFi = (CheckBoxPreference) preferenceScreen.findPreference(KEY_WIFI);
        mPreferenceButtonFirst = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_FIRST);
        mPreferenceButtonSecond = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_SECOND);
        mPreferenceButtonThird = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_THIRD);
        mPreferenceButtonFourth = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_FOURTH);
        mPreferenceButtonFifth = (ListPreference) preferenceScreen.findPreference(Preferences.KEY_BUTTON_FIFTH);
        mProgressCategory = (ProgressCategory) preferenceScreen.findPreference(KEY_SERVERS);
        CheckBoxPreference preferencePauseForCall = (CheckBoxPreference) preferenceScreen.findPreference(KEY_PAUSE_FOR_CALL);
        EditTextPreference  preferenceSeekTime = (EditTextPreference) preferenceScreen.findPreference(Preferences.KEY_SEEK_TIME);
        EditTextPreference  preferenceAudioDelay = (EditTextPreference) preferenceScreen.findPreference(Preferences.KEY_AUDIO_DELAY);
        EditTextPreference  preferenceSubtitleDelay = (EditTextPreference) preferenceScreen.findPreference(Preferences.KEY_SUBTITLE_DELAY);
        
        mPreferenceButtonFirst.setOnPreferenceChangeListener(this);
        mPreferenceButtonSecond.setOnPreferenceChangeListener(this);
        mPreferenceButtonThird.setOnPreferenceChangeListener(this);
        mPreferenceButtonFourth.setOnPreferenceChangeListener(this);
        mPreferenceButtonFifth.setOnPreferenceChangeListener(this);
        preferenceAudioDelay.setOnPreferenceChangeListener(this);
        preferenceSubtitleDelay.setOnPreferenceChangeListener(this);
        preferenceSeekTime.setOnPreferenceChangeListener(this);
        preferencePauseForCall.setOnPreferenceChangeListener(this);
        preferencePauseForCall.setChecked(getPauseForCall());

        Intent intent = getActivity().getIntent();
        mPort = intent.getIntExtra(PortSweeper.EXTRA_PORT, 0);
        if (mPort == 0) {
            throw new IllegalArgumentException("Port must be specified");
        }
        
        mRemembered = buildRememberedServersMap(Preferences.get(getActivity()).getRememberedServers());
        
        mReceiver = new MyBroadcastReceiver();
        setButtonIcons();
        updateWifiInfo();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = super.onCreateView(inflater, container, savedInstanceState);
        registerForContextMenu(v.findViewById(android.R.id.list));
        return v;
    }

    @Override
    public void onResume() {
        super.onResume();
        IntentFilter filter = new IntentFilter();
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
        filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        getActivity().registerReceiver(mReceiver, filter);
    }

    @Override
    public void onPause() {
        super.onPause();
        getActivity().unregisterReceiver(mReceiver);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        ((PickServerActivity) getActivity()).setServerInfoDialogListener(null);
    }
    
    private Map<String, Server> buildRememberedServersMap(List<String> keyList) {
        Map<String, Server> map = new HashMap<String, Server>(keyList.size());
        for(String key : keyList) {
            Server s = Server.fromKey(key);
            map.put(s.getUri().getAuthority(), s);
        }
        return map;
    }
    
    private ArrayList<String> buildRememberedServers() {
        ArrayList<String> list = new ArrayList<String>(mRemembered.size());
        for(Server server : mRemembered.values()) {
            list.add(server.toKey());
        }
        return list;
    }
    
    private Preference createServerPreference(Server server) {
        Preference preference = new Preference(getActivity());
        preference.setKey(server.toKey());
        preference.setTitle(server.getNickname().isEmpty() ? server.getHostAndPort() : server.getNickname());
        preference.setPersistent(false);
        String authority = server.getUri().getAuthority();
        if(mRemembered.containsKey(authority) && authority.equals(Preferences.get(getActivity()).getAuthority())) {
            preference.setWidgetLayoutResource(R.layout.tick_image);
        }
        switch (server.getResponseCode()) {
            case HttpURLConnection.HTTP_UNAUTHORIZED:
                preference.setSummary(R.string.summary_unauthorized);
                preference.setIcon(R.drawable.ic_vlc_server_auth);
                break;
            case HttpURLConnection.HTTP_FORBIDDEN:
                preference.setSummary(R.string.summary_forbidden);
                preference.setIcon(R.drawable.ic_vlc_server_forbidden);
                break;
            default:
                if(server.hasUserInfo()) {
                    preference.setIcon(R.drawable.ic_vlc_server_auth);
                } else {
                    preference.setIcon(R.drawable.ic_vlc_server);
                }
        }
        return preference;
    }

    private WifiInfo getConnectionInfo() {
        WifiManager manager = (WifiManager) getActivity().getSystemService(Activity.WIFI_SERVICE);
        WifiInfo info = manager.getConnectionInfo();
        if (info != null) {
            SupplicantState state = info.getSupplicantState();
            if (state.equals(SupplicantState.COMPLETED)) {
                return info;
            }
        }
        return null;
    }

    @Override
    public void onProgress(int progress, int max) {
        if(getActivity() == null) {
            return;
        }
        if (progress == 0) {
            mProgressCategory.removeAll();
            for (String authority : mRemembered.keySet()) {
                Preference preference = createServerPreference(mRemembered.get(authority));
                preference.setSummary(R.string.summary_remembered);
                mProgressCategory.addPreference(preference);
            }
        }
        mProgressCategory.setProgress(progress != max);
    }

    @Override
    public void onHostFound(String hostname, int responseCode) {
        Server server = new Server(hostname + ":" + mPort, responseCode);
        switch (responseCode) {
            case HttpURLConnection.HTTP_OK:
            case HttpURLConnection.HTTP_FORBIDDEN:
            case HttpURLConnection.HTTP_UNAUTHORIZED:
                if (!mRemembered.containsKey(server.getUri().getAuthority()) && getActivity() != null) {
                    Preference preference = createServerPreference(server);
                    mProgressCategory.addPreference(preference);
                }
                break;
            default:
                Log.d(TAG, "Unexpected response code: " + responseCode);
                break;
        }
    }
    
    @Override
    public void onAddServer(Server server) {
        Intent data = new Intent();
        data.setData(server.getUri());
        if (!mRemembered.containsKey(server.getUri().getAuthority())) {
            mRemembered.put(server.getUri().getAuthority(), server);
        }
        Preferences.get(getActivity()).setRememberedServers(buildRememberedServers());
        getActivity().setResult(Activity.RESULT_OK, data);
        getActivity().finish();
    }
    
    @Override
    public void onEditServer(Server newServer, String oldServerKey) {
        String oldAuthority = Server.fromKey(oldServerKey).getUri().getAuthority();
        if(mRemembered.containsKey(oldAuthority)) {
            mRemembered.remove(oldAuthority);
            mRemembered.put(newServer.getUri().getAuthority(), newServer);
        }
        Preferences.get(getActivity()).setRememberedServers(buildRememberedServers());
    }
    
    private void forget(Server server) {
        mRemembered.remove(server.getUri().getAuthority());
        int count = mProgressCategory.getPreferenceCount();
        for (int i = 0; i < count; i++) {
            Preference preference = mProgressCategory.getPreference(i);
            if (server.toKey().equals(preference.getKey())) {
                mProgressCategory.removePreference(preference);
                break;
            }
        }
        Preferences.get(getActivity()).setRememberedServers(buildRememberedServers());
    }
    
    private boolean isHandledByChangeListener(String preferenceKey) {
        return KEY_SCREEN_INTERFACE.equals(preferenceKey)            ||
               KEY_SCREEN_PRESETS.equals(preferenceKey)              ||
               KEY_SCREEN_BUTTONS.equals(preferenceKey)              ||
               KEY_SCREEN_NOTIFICATION.equals(preferenceKey)         ||
               KEY_PAUSE_FOR_CALL.contains(preferenceKey)            ||
               Preferences.isButtonKey(preferenceKey)                ||
               Preferences.KEY_SEEK_TIME.equals(preferenceKey)       ||
               Preferences.KEY_AUDIO_DELAY.equals(preferenceKey)     ||
               Preferences.KEY_SUBTITLE_DELAY.equals(preferenceKey);
    }

    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
        if(isHandledByChangeListener(preference.getKey())) {
            return super.onPreferenceTreeClick(preferenceScreen, preference);
        } else if(Preferences.KEY_HIDE_DVD_TAB.equals(preference.getKey())) {
            Preferences.get(getActivity()).setHideDVDTab(((CheckBoxPreference) preference).isChecked());
            return true;
        } else if(Preferences.KEY_SORT_DIRECTORIES_FIRST.equals(preference.getKey())) {
            Preferences.get(getActivity()).setSortDirectoriesFirst(((CheckBoxPreference) preference).isChecked());
            return true;
        } else if(Preferences.KEY_PARSE_PLAYLIST_ITEMS.equals(preference.getKey())) {
            Preferences.get(getActivity()).setParsePlaylistItems(((CheckBoxPreference) preference).isChecked());
            return true;
        } else if (Preferences.KEY_SERVER_SUBTITLE.equals(preference.getKey())) {
            Preferences.get(getActivity()).setServerSubtitle(((CheckBoxPreference) preference).isChecked());
            return true;
        } else if (Preferences.KEY_NOTIFICATION.equals(preference.getKey())) {
            boolean isChecked = ((CheckBoxPreference) preference).isChecked();
            Intent i = Intents.service(getActivity(), isChecked ? Intents.ACTION_NOTIFICATION_CREATE : Intents.ACTION_NOTIFICATION_CANCEL);
            Preferences.get(getActivity()).setNotification(isChecked);
            getActivity().startService(i);
            return true;
        } else if(Preferences.KEY_NOTIFICATION_ICON_TRANSPARENT.equals(preference.getKey())) {
            Preferences.get(getActivity()).setNotificationIconTransparent(((CheckBoxPreference) preference).isChecked());
            getActivity().startService(Intents.service(getActivity(), Intents.ACTION_NOTIFICATION_CREATE));
            return true;
        } else if(KEY_ADD_SERVER.equals(preference.getKey())) {
            ServerInfoDialog.addServerInstance().show(getFragmentManager(), DIALOG_ADD_SERVER);
            return true;
        } else if (KEY_WIFI.equals(preference.getKey())) {
            startActivity(new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK));
            updateWifiInfo(); // Undo checkbox toggle
            return true;
        } else {
            Server server = Server.fromKey(preference.getKey());
            if(server.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
                ServerInfoDialog.addAuthServerInstance(server.toKey()).show(getFragmentManager(), DIALOG_ADD_SERVER);
            } else {
                onAddServer(server);
            }
            return true;
        }
    }
    
    /** {@inheritDoc} */
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        if (KEY_PAUSE_FOR_CALL.equals(preference.getKey())) {
            setPauseForCall(Boolean.TRUE.equals(newValue));
            return true;
        } else if(Preferences.KEY_SEEK_TIME.equals(preference.getKey())) {
            if(isNumericOrToast(preference, (String) newValue, "The seek time entered is not valid")) {
                Preferences.get(getActivity()).setSeekTime((String) newValue);
                return true;
            }
            return false;
        } else if(Preferences.KEY_AUDIO_DELAY.equals(preference.getKey())) {
            if(isNumericOrToast(preference, (String) newValue, "The audio delay entered is not valid")) {
                Preferences.get(getActivity()).setAudioDelayToggle(Integer.valueOf((String) newValue));
                return true;
            }
            return false;
        } else if(Preferences.KEY_SUBTITLE_DELAY.equals(preference.getKey())) {
            if(isNumericOrToast(preference, (String) newValue, "The subtitle delay entered is not valid")) {
                Preferences.get(getActivity()).setSubtitleDelayToggle(Integer.valueOf((String) newValue));
                return true;
            }
            return false;
        } else if(Preferences.isButtonKey(preference.getKey())) {
            Preferences.get(getActivity()).setButton(preference.getKey(), (String) newValue);
            setButtonIcon(preference, (String) newValue);
            return true;
        }
        return false;
    }
    
    private boolean isNumericOrToast(Preference preference, String value, String toastMessage) {
        try {
            Long.parseLong(value);
            return true;
        } catch(NumberFormatException e) {
            Toast.makeText(getActivity(), toastMessage, Toast.LENGTH_SHORT).show();
            return false;
        }
    }
    
    private void setButtonIcon(Preference preference, String button) {
        preference.setIcon(Buttons.getButton(preference.getKey(), button).getIconId());
    }
    
    private void setButtonIcons() {
        Preferences p = Preferences.get(getActivity());
        mPreferenceButtonFirst.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_FIRST, p).getIconId());
        mPreferenceButtonSecond.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_SECOND, p).getIconId());
        mPreferenceButtonThird.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_THIRD, p).getIconId());
        mPreferenceButtonFourth.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_FOURTH, p).getIconId());
        mPreferenceButtonFifth.setIcon(Buttons.getButton(Preferences.KEY_BUTTON_FIFTH, p).getIconId());
    }
    

    private Preference getPreferenceFromMenuInfo(ContextMenuInfo menuInfo) {
        if (menuInfo != null) {
            if (menuInfo instanceof AdapterContextMenuInfo) {
                AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
                PreferenceScreen screen = getPreferenceScreen();
                ListAdapter root = screen.getRootAdapter();
                Object item = root.getItem(adapterMenuInfo.position);
                return (Preference) item;
            }
        }
        return null;
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        Preference preference = getPreferenceFromMenuInfo(menuInfo);
        if (preference != null) {
            String authority = Server.fromKey(preference.getKey()).getUri().getAuthority();
            if (mRemembered.containsKey(authority)) {
                menu.add(Menu.NONE, CONTEXT_EDIT_SERVER, Menu.NONE, R.string.edit_server);
                menu.add(Menu.NONE, CONTEXT_FORGET, Menu.NONE, R.string.context_forget);
            }
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        Preference preference;
        switch (item.getItemId()) {
            case CONTEXT_FORGET:
                preference = getPreferenceFromMenuInfo(item.getMenuInfo());
                if (preference != null) {
                    forget(Server.fromKey(preference.getKey()));
                }
                return true;
            case CONTEXT_EDIT_SERVER:
                preference = getPreferenceFromMenuInfo(item.getMenuInfo());
                if(preference != null) {
                    ServerInfoDialog d = ServerInfoDialog.editServerInstance(preference.getKey());
                    d.show(getFragmentManager(), DIALOG_EDIT_SERVER);
                }
                return true;
            default:
                return super.onContextItemSelected(item);
        }
    }

    private void updateWifiInfo() {
        WifiInfo info = getConnectionInfo();
        if (info != null) {
            mPreferenceWiFi.setChecked(true);
            String ssid = info.getSSID();
            String template = getString(R.string.summary_wifi_connected);
            Object[] objects = {
                ssid != null ? ssid : ""
            };
            mPreferenceWiFi.setSummary(MessageFormat.format(template, objects));
        } else {
            mPreferenceWiFi.setChecked(false);
            mPreferenceWiFi.setSummary(R.string.summary_wifi_disconnected);
        }
    }
    
    private boolean getPauseForCall() {
        switch (getActivity().getPackageManager().getComponentEnabledSetting(PHONE_STATE_RECEIVER)) {
            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
                return true;
            default:
                return false;
        }
    }

    private void setPauseForCall(boolean enabled) {
        getActivity().getPackageManager().setComponentEnabledSetting(
                PHONE_STATE_RECEIVER,
                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }
    
    private class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            updateWifiInfo();
        }
    }

}