/*
 * Copyright 2015 Schedo Inc. All rights reserved.
 *
 * 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.ncode.android.apps.schedo.nearby;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import android.util.Patterns;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import static com.ncode.android.apps.schedo.Config.METADATA_URL;
import static com.ncode.android.apps.schedo.util.LogUtils.makeLogTag;

/**
 * Class for resolving BLE device metadata via proxy.
 */
public class MetadataResolver {
    static String TAG = makeLogTag("MetadataResolver");

    static Map<String, String> mDeviceUrlMap;
    static RequestQueue mRequestQueue;

    static boolean mIsInitialized = false;

    public static void initialize(Context context) {
        mDeviceUrlMap = new HashMap<String, String>();
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(context);
        }

        mIsInitialized = true;
    }

    public static String getURLForDevice(NearbyDevice device) {
        if (!mIsInitialized) {
            Log.e(TAG, "Not initialized.");
            return null;
        }

        // If the device name is already a URL, use it.
        String deviceName = device.getName();
        String url = deviceName;
        if (Patterns.WEB_URL.matcher(deviceName).matches()) {
            // TODO: Improve this.
            // For now, if there's no scheme present, add a default http:// scheme.
            if (!url.startsWith("http://") && !url.startsWith("https://")) {
                url = "http://" + url;
            }
        } else {
            // Otherwise, try doing the lookup.
            url = mDeviceUrlMap.get(deviceName);
        }
        return url;
    }

    public static void getBatchMetadata(List<NearbyDevice> batchList) {
        if (!mIsInitialized) {
            Log.e(TAG, "Not initialized.");
            return;
        }

        JSONObject jsonObj = createRequestObject(batchList);

        Map<String, NearbyDevice> deviceMap = new HashMap<String, NearbyDevice>();

        for (int i = 0; i < batchList.size(); i++) {
            NearbyDevice nearbyDevice = batchList.get(i);
            deviceMap.put(nearbyDevice.getUrl(), nearbyDevice);
        }

        JsonObjectRequest jsObjRequest = createMetadataRequest(jsonObj, deviceMap);

        // Queue the request
        mRequestQueue.add(jsObjRequest);
    }

    private static JsonObjectRequest createMetadataRequest(JSONObject jsonObj, final Map<String, NearbyDevice> deviceMap) {
        return new JsonObjectRequest(
                METADATA_URL,
                jsonObj,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject jsonResponse) {

                        try {
                            JSONArray foundMetaData = jsonResponse.getJSONArray("metadata");

                            int deviceCount = foundMetaData.length();
                            for (int i = 0; i < deviceCount; i++) {

                                JSONObject deviceData = foundMetaData.getJSONObject(i);

                                String title = "Unknown name";
                                String url = "Unknown url";
                                String description = "Unknown description";
                                String iconUrl = "/favicon.ico";
                                String id = deviceData.getString("id");

                                if (deviceData.has("title")) {
                                    title = deviceData.getString("title");
                                }
                                if (deviceData.has("url")) {
                                    url = deviceData.getString("url");
                                }
                                if (deviceData.has("description")) {
                                    description = deviceData.getString("description");
                                }
                                if (deviceData.has("icon")) {
                                    // We might need to do some magic here.
                                    iconUrl = deviceData.getString("icon");
                                }
                                // Provisions for a favicon specified as a relative URL.
                                if (!iconUrl.startsWith("http")) {
                                    // Lets just assume we are dealing with a relative path.
                                    Uri fullUri = Uri.parse(url);
                                    Uri.Builder builder = fullUri.buildUpon();
                                    // Append the default favicon path to the URL.
                                    builder.path(iconUrl);
                                    iconUrl = builder.toString();
                                }

                                DeviceMetadata deviceMetadata = new DeviceMetadata();
                                deviceMetadata.title = title;
                                deviceMetadata.description = description;
                                deviceMetadata.siteUrl = url;
                                deviceMetadata.iconUrl = iconUrl;
                                downloadIcon(deviceMetadata, deviceMap.get(id));

                                // Look up the device from the input and update the data
                                deviceMap.get(id).onDeviceInfo(deviceMetadata);
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {
                        Log.i(TAG, "VolleyError: " + volleyError.toString());
                    }
                }
        );
    }

    private static JSONObject createRequestObject(List<NearbyDevice> devices) {
        JSONObject jsonObj = new JSONObject();

        try {
            JSONArray urlArray = new JSONArray();

            for(int dIdx = 0; dIdx < devices.size(); dIdx++) {
                NearbyDevice device = devices.get(dIdx);

                JSONObject urlObject = new JSONObject();

                urlObject.put("url", device.getUrl());
                urlObject.put("rssi", device.getLastRSSI());
                urlArray.put(urlObject);
            }


            JSONObject location = new JSONObject();

            location.put("lat", 49.129837);
            location.put("lon", 120.38142);

            jsonObj.put("location",  location);
            jsonObj.put("objects", urlArray);

        } catch(JSONException ex) {

        }
        return jsonObj;
    }

    /**
     * Asynchronously download the image for the nearby device.
     * @param metadata
     * @param listener
     */
    private static void downloadIcon(final DeviceMetadata metadata, final OnMetadataListener listener) {
        ImageRequest imageRequest = new ImageRequest(metadata.iconUrl, new Response.Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap response) {
                Log.i(TAG, "Got an image: " + response.toString());
                metadata.icon = response;
                listener.onDeviceInfo(metadata);
            }
        }, 0, 0, null, null);
        mRequestQueue.add(imageRequest);
    }

    public interface OnMetadataListener {
        public void onDeviceInfo(DeviceMetadata deviceMetadata);
    }
}
