// Copyright 2015 The Project Buendia Authors
//
// 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 distrib-
// uted 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
// specific language governing permissions and limitations under the License.

package org.projectbuendia.client.net;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Response;
import com.android.volley.toolbox.JsonObjectRequest;

import org.json.JSONObject;
import org.projectbuendia.client.utils.Logger;
import org.projectbuendia.client.utils.Utils;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

/** Base class for authenticated OpenMRS JSON requests. */
public class OpenMrsJsonRequest extends JsonObjectRequest {
    private final String mUsername;
    private final String mPassword;

    private static Logger LOG = Logger.create();

    /**
     * Constructs a GET request to OpenMRS.
     * @param connectionDetails an {@link OpenMrsConnectionDetails} for communicating with OpenMRS
     * @param urlSuffix         the API URL being requested, relative to the API root
     * @param jsonRequest       a {@link JSONObject} containing the request body
     * @param listener          a {@link Response.Listener} that handles successful requests
     * @param errorListener     a {@link Response.ErrorListener} that handles failed requests
     */
    public OpenMrsJsonRequest(OpenMrsConnectionDetails connectionDetails,
                              String urlSuffix,
                              JSONObject jsonRequest,
                              Response.Listener<JSONObject> listener,
                              Response.ErrorListener errorListener) {
        this(connectionDetails.getUser(), connectionDetails.getPassword(),
            connectionDetails.getBuendiaApiUrl() + urlSuffix,
            jsonRequest, listener, errorListener);
    }

    private OpenMrsJsonRequest(String username, String password, String url, JSONObject jsonRequest,
                               Response.Listener<JSONObject> listener,
                               Response.ErrorListener errorListener) {
        super(url, jsonRequest, listener, errorListener);
        this.mUsername = username;
        this.mPassword = password;
    }

    /**
     * Constructs a request to OpenMRS using an arbitrary HTTP method.
     * @param connectionDetails an {@link OpenMrsConnectionDetails} for communicating with OpenMRS
     * @param method            the HTTP method
     * @param urlSuffix         the API URL being requested, relative to the API root
     * @param jsonRequest       a {@link JSONObject} containing the request body
     * @param listener          a {@link Response.Listener} that handles successful requests
     * @param errorListener     a {@link Response.ErrorListener} that handles failed requests
     */
    public OpenMrsJsonRequest(OpenMrsConnectionDetails connectionDetails,
                              int method, String urlSuffix, JSONObject jsonRequest,
                              Response.Listener<JSONObject> listener,
                              Response.ErrorListener errorListener) {
        super(method, connectionDetails.getBuendiaApiUrl() + urlSuffix,
            jsonRequest, listener, errorListener);
        this.mUsername = connectionDetails.getUser();
        this.mPassword = connectionDetails.getPassword();
    }

    @Override public Map<String, String> getHeaders() throws AuthFailureError {
        // TODO: work out how to do Auth properly
        HashMap<String, String> params = new HashMap<>();
        OpenMrsConnectionDetails.addAuthHeader(mUsername, mPassword, params);
        params.put("Connection-Type", "application/json");
        return params;
    }

    @Override
    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        LOG.finish("HTTP." + getSequence(), "Response to %s -> %s", Utils.repr(this), Utils.repr(response.data, 500));
        try {
            if (response.data.length == 0) {
                byte[] responseData = "{}".getBytes("UTF8");
                response = new NetworkResponse(
                        response.statusCode, responseData, response.headers, response.notModified);
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Should never occur");
        }
        return super.parseNetworkResponse(response);
    }
}

@@ -831,16 +831,62 @@
 Object;%0A
+import org.projectbuendia.client.BuildConfig;%0A
 import o
@@ -1065,16 +1065,73 @@
 l.Map;%0A%0A
+import static org.projectbuendia.client.utils.Utils.eq;%0A%0A
 /** Base
@@ -4262,20 +4262,16 @@
 Override
-%0A   
  protect
@@ -4339,24 +4339,24 @@
 response) %7B%0A
+
         LOG.
@@ -4630,20 +4630,16 @@
 sponse(%0A
-    
         
@@ -4726,16 +4726,16 @@
 ified);%0A
+
         
@@ -4875,51 +4875,1135 @@
     
-return super.parseNetworkResponse(response)
+String requiredVersion = getMinimumVersionHeader(response);%0A        LOG.d(%22Client version is %25s; server requires %25s%22,%0A            BuildConfig.VERSION_NAME, requiredVersion);%0A        if (requiredVersion != null && isProductionVersion(BuildConfig.VERSION_NAME)) %7B%0A            if (Utils.ALPHANUMERIC_COMPARATOR.compare(%0A                BuildConfig.VERSION_NAME, requiredVersion) %3C 0) %7B%0A                LOG.e(%22Client version is %25s, but server requires at least %25s%22,%0A                    BuildConfig.VERSION_NAME, requiredVersion);%0A            %7D%0A        %7D%0A        return super.parseNetworkResponse(response);%0A    %7D%0A%0A    private boolean isProductionVersion(String version) %7B%0A        return version.contains(%22.%22);%0A    %7D%0A%0A    private String getMinimumVersionHeader(NetworkResponse response) %7B%0A        // HTTP headers are case-insensitive but NetworkResponse stores%0A        // them in a case-sensitive Map.  Sigh.%0A        for (String key : response.headers.keySet()) %7B%0A            if (eq(key.toLowerCase(), %22buendia-client-minimum-version%22)) %7B%0A                return response.headers.get(key);%0A            %7D%0A        %7D%0A        return null
 ;%0A  

Check the server's requirement for client minimum version.