package es.stratebi.civi.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

public class CiviRestService {
    private String callUrl = "";
    private String restUrl = "";
    private String apiKey = "";
    private String key = "";
    private String action = "get";
    private String fieldsAction = "getfields";
    private String entity = "Contact";
    private String options = "";
    private int limit = 25;
    private long offset = 0;
    private boolean isFirst = true;
    private boolean isError = false;
    private boolean isDebug = false;
    private BufferedWriter sockedWriter;
    private BufferedReader socketReader;
    private Socket civiSocket;
    JSONParser parser = new JSONParser();
    private ArrayList<String> noInputActions = new ArrayList<String>();
    private ArrayList<String> noOutputActions = new ArrayList<String>();

    private HashMap<String, CiviField> entityFields = new HashMap<String, CiviField>();

    {
        this.noInputActions.add("getfields");
        this.noInputActions.add("getactions");
        this.noInputActions.add("create");
        this.noInputActions.add("update");
        this.noInputActions.add("replace");

        this.noInputActions.add("delete");                        // Verificar  -- Contact
        this.noInputActions.add("quicksearch");                   // Verificar  -- Contact
        this.noInputActions.add("activity_create");               // Verificar  -- Case
        this.noInputActions.add("merge");                         // Verificar  -- Contact
        this.noInputActions.add("transact");                      // Verificar  -- Contribution
        this.noInputActions.add("sendconfirmation");              // Verificar  -- Contribution
        this.noInputActions.add("install");                       // Verificar  -- Extension
        this.noInputActions.add("enable");                        // Verificar  -- Extension
        this.noInputActions.add("disable");                       // Verificar  -- Extension
        this.noInputActions.add("uninstall");                     // Verificar  -- Extension
        this.noInputActions.add("download");                      // Verificar  -- Extension
        this.noInputActions.add("refresh");                       // Verificar  -- Extension
        this.noInputActions.add("execute");                       // Verificar  -- Job
        this.noInputActions.add("geocode");                       // Verificar  -- Job
        this.noInputActions.add("send_reminder");                 // Verificar  -- Job
        this.noInputActions.add("update_greeting");               // Verificar  -- Job
        this.noInputActions.add("process_pledge");                // Verificar  -- Job
        this.noInputActions.add("process_mailing");               // Verificar  -- Job
        this.noInputActions.add("process_sms");                   // Verificar  -- Job
        this.noInputActions.add("fetch_bounces");                 // Verificar  -- Job
        this.noInputActions.add("fetch_activities");              // Verificar  -- Job
        this.noInputActions.add("process_participant");           // Verificar  -- Job
        this.noInputActions.add("process_membership");            // Verificar  -- Job
        this.noInputActions.add("process_respondent");            // Verificar  -- Job
        this.noInputActions.add("process_batch_merge");           // Verificar  -- Job
        this.noInputActions.add("run_payment_cron");              // Verificar  -- Job
        this.noInputActions.add("cleanup");                       // Verificar  -- Job
        this.noInputActions.add("disable_expired_relationships"); // Verificar  -- Job
        this.noInputActions.add("group_rebuild");                 // Verificar  -- Job
        this.noInputActions.add("mail_report");                   // Verificar  -- Job
        this.noInputActions.add("event_bounce");                  // Verificar  -- Mailing
        this.noInputActions.add("event_confirm");                 // Verificar  -- Mailing
        this.noInputActions.add("event_bounce");                  // Verificar  -- Mailing
        this.noInputActions.add("event_reply");                   // Verificar  -- Mailing
        this.noInputActions.add("event_forward");                 // Verificar  -- Mailing
        this.noInputActions.add("event_click");                   // Verificar  -- Mailing
        this.noInputActions.add("event_open");                    // Verificar  -- Mailing
        this.noInputActions.add("update_email_resetdate");        // Verificar  -- Mailing
        this.noInputActions.add("tree_get");                      // Verificar  -- Note
        this.noInputActions.add("set");                           // Verificar  -- Profile
        this.noInputActions.add("apply");                         // Verificar  -- Profile
        this.noInputActions.add("revert");                        // Verificar  -- Setting
        this.noInputActions.add("fill");                          // Verificar  -- Setting

        this.noOutputActions.add("get");
        this.noOutputActions.add("getfields");
        this.noOutputActions.add("getquick");
        this.noOutputActions.add("getactions");
        this.noOutputActions.add("getoptions");
        this.noOutputActions.add("getvalue");
        this.noOutputActions.add("getsingle");
        this.noOutputActions.add("getcount");
        this.noOutputActions.add("update");
        this.noOutputActions.add("replace");
    }

    private ArrayList<String> entityList = new ArrayList<String>();;
    private Long entityCount = 0L;
    private Boolean returnEmptyOnFail;
    private String civiJsonError = "";

    public CiviRestService(String restUrl, String apiKey, String key, String entity) {
        this.restUrl = restUrl;
        this.apiKey = apiKey;
        this.key = key;
        this.entity = entity;
    }

    public CiviRestService(String restUrl, String apiKey, String key, String action, String entity) {
        this.restUrl = restUrl;
        this.apiKey = apiKey;
        this.key = key;
        this.action = action;
        this.entity = entity;
    }

    public String getRestUrl() {
        return restUrl;
    }

    public void setRestUrl(String restUrl) {
        this.restUrl = restUrl;
    }

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getFieldsAction() {
        return fieldsAction;
    }

    public void setFieldsAction(String fieldsAction) {
        this.fieldsAction = fieldsAction;
    }

    public String getEntity() {
        return entity;
    }

    public void setEntity(String entity) {
        this.entity = entity;
    }

    public String getOptions() {
        return options;
    }

    public void setOptions(String options) {
        this.options = options;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(Integer limit) {
        this.limit = limit;
    }

    public long getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public boolean isFirst() {
        return isFirst;
    }

    public void setFirst(boolean isFirst) {
        this.isFirst = isFirst;
    }

    public HashMap<String, CiviField> getEntityFields() {
        return entityFields;
    }

    public void setEntityFields(HashMap<String, CiviField> entityFields) {
        this.entityFields = entityFields;
    }

    public boolean isError() {
        return isError;
    }

    public void setError(boolean isError) {
        this.isError = isError;
    }

    public HttpURLConnection getHttpConnection(String method, URL url) throws IOException {
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod(method);
        conn.setRequestProperty("Accept", "application/json");

        if (conn.getResponseCode() != 200) {
            throw new RuntimeException("HTTP error: " + conn.getResponseCode());
        }

        return conn;
    }

    public String getCiviResponse(HttpURLConnection conn) throws IOException {
        String response = "";

        InputStream input = conn.getInputStream();
        InputStreamReader streamReader = new InputStreamReader(input);
        BufferedReader br = new BufferedReader(streamReader);

        String line = "";

        while ((line = br.readLine()) != null) {
            response = response.concat(line);
        }

        return response;
    }

    public URL getUrl(String entity, String action, String params) throws MalformedURLException {
        String urlString = "";

        urlString = restUrl + "?api_key=".concat(apiKey);
        urlString = urlString + "&key=".concat(key);

        urlString = urlString.concat("&json=1");
        urlString = urlString.concat("&debug=0");
        urlString = urlString.concat("&version=3");

        urlString = urlString + "&entity=".concat(entity);
        urlString = urlString + "&action=".concat(action);

        urlString = urlString + params;

        URL url = new URL(urlString);

        return url;
    }

    public boolean isValidJSONObject(String json) {
        boolean valid;

        try {
            Object obj = parser.parse(json);
            //new JSONObject(json);
            valid = true;
        } catch (ParseException e) {
            valid = false;
            e.printStackTrace();
        }

        return valid;
    }

    public HashMap<String, CiviField> getFieldList(boolean reset) throws IOException, CiviCRMException {
        if (reset)
            getFields();

        return entityFields;
    }

    private void getFields() throws IOException, CiviCRMException {
        try {
            String params = "&options[limit]=0";
            getEntityList(false);

            if (!entityList.contains(this.entity)) {
                throw new CiviCRMException("CiviCRM Error: Entity [" + this.entity + "] not exists ");
            } else {
                URL url = getUrl(this.entity, this.fieldsAction, params);
                HttpURLConnection conn = getHttpConnection("GET", url);
                String json = getCiviResponse(conn);

                JSONObject jsonObject = (JSONObject) parser.parse(json);

                // Aqui debo chequear si hay un error de acceso
                if ((Long)jsonObject.get("is_error") == 1L) {
                    throw new CiviCRMException("CiviCRM API Error: " + jsonObject.get("error_message").toString());
                }
                //else if ((Long)jsonObject.get("is_error") == 0L && (Long)jsonObject.get("count") == 0L) {
                //}
                else {
                    entityFields = new HashMap<String, CiviField>();
                    Object values = jsonObject.get("values");
                    if (values.getClass().getSimpleName().equals("JSONObject")) {
                        JSONObject jsonFields = (JSONObject)values;
                        for (Object key : jsonFields.keySet()) {
                            String fieldName = (String) key;
                            JSONObject jsonField = (JSONObject) jsonFields.get(key);
                            try {
                                entityFields.put(fieldName, new CiviField(fieldName, jsonField));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    } else if (values.getClass().getSimpleName().equals("JSONArray")) {
//                        JSONObject jsonField = new JSONObject();
//                        jsonField.put("name", this.entity);
//                        jsonField.put("type", "String");
//                        entityFields.put(this.entity, new CiviField(this.entity, jsonField));
                        /*
                         * Entity: ActivityType            fieldsArray size: 0
                         * Entity: CustomSearch            fieldsArray size: 0
                         * Entity: Entity                  fieldsArray size: 0
                         * Entity: Location                fieldsArray size: 0
                         * Entity: MailingEventConfirm     fieldsArray size: 0
                         * Entity: MailingEventResubscribe fieldsArray size: 0
                         * Entity: MailingEventSubscribe   fieldsArray size: 0
                         * Entity: MailingEventUnsubscribe fieldsArray size: 0
                         * Entity: PriceField              fieldsArray size: 0
                         * Entity: PriceFieldValue         fieldsArray size: 0
                         * Entity: PriceSet                fieldsArray size: 0
                         * Entity: ReportTemplate          fieldsArray size: 0
                         * Entity: SurveyRespondant        fieldsArray size: 0
                         * Entity: System                  fieldsArray size: 0
                         */
                    }

                    // Llamar al get de la entidad limitado a un registro para ver que campos devuelve
                    // y eliminar de entityFields todos aquellos que no esten en el resultado
                    // y añadir los que no estén. En ActivityType no hay campos pero si hay un resultado
                    // updateReturningFields();
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        }
    }

    public void updateReturningFields(HashMap<String, CiviField> fieldListHashMap, HashMap<String, CiviField> filterFieldHashMap) throws IOException, CiviCRMException, ParseException {
        ArrayList<String> entityKeyList = new ArrayList<String>();
        ArrayList<String> extraFieldList = new ArrayList<String>();
        String params = "&options[limit]=1&options[offset]=1";

        URL url = getUrl(entity, "get", params);
        HttpURLConnection conn = getHttpConnection("GET", url);

        String jsonResponse = getCiviResponse(conn);

        JSONObject jsonObject = (JSONObject)parser.parse(jsonResponse);

        if ((Long)jsonObject.get("is_error") == 1L) {
            throw new CiviCRMException("CiviCRM API Error: " + jsonObject.get("error_message").toString());
        } else {
            Object valueField = jsonObject.get("values");
            Object fieldValue;

            if(valueField.getClass().getSimpleName().equals("JSONObject")) {
                JSONObject fieldSet = (JSONObject) valueField;
                //boolean is_rowset = false;
                for (Object key: fieldSet.keySet()) {
                    HashMap<String, Object> fieldValues = new HashMap<String, Object>();
                    Object field = fieldSet.get(key);
                    if (field.getClass().getSimpleName().equals("JSONObject")) {
                        // MailSettings
                        // {"object1":{"key1":"Value1","key2":"Value2", ...},"object2":{"key1":"Value1","key2":"Value2", ...},...}
                        JSONObject jsonField = (JSONObject) field;
                        for (Object fieldName: jsonField.keySet()) {
                            entityKeyList.add((String) fieldName);
                        }

                    } else if (field.getClass().getSimpleName().equals("String")) {
                        // ActivityType
                        // {"id1":"Value1","id2":"Value2", ...}
                        entityKeyList.add("ActivityTypeId");
                        entityKeyList.add((String) this.entity);
                    }
                }
            } else if (valueField.getClass().getSimpleName().equals("JSONArray")) {
                JSONArray values = (JSONArray)jsonObject.get("values");

                Iterator<Object> iterator = values.iterator();

                while (iterator.hasNext()) {
                    Object entityObject = iterator.next();
                    HashMap<String, Object> fieldValues = new HashMap<String, Object>();

                    if (entityObject.getClass().getSimpleName().equals("JSONObject")) {
                        // File
                        // [{"key1":"Value1","key2":"Value2", ...},{"key1":"Value1","key2":"Value2", ...},...]
                        JSONObject jsonArrayField = (JSONObject) entityObject;
                        for (Object fieldName: jsonArrayField.keySet()) {
                            entityKeyList.add((String) this.entity);
                        }
                    } else if (entityObject.getClass().getSimpleName().equals("String")) {
                        // Entity
                        // ["Activity","ActivityType","Address", ...]
                        entityKeyList.add("Entity");
                    }
                }
            }
        }

        extraFieldList.addAll(entityKeyList);
        filterFieldHashMap.clear();
        filterFieldHashMap.putAll(fieldListHashMap);

        HashMap<String, CiviField> tmpFieldsMap = new HashMap<String, CiviField>();
        tmpFieldsMap.putAll(fieldListHashMap);
        // Obteniendo listado de campos del getfields no devueltos en el get
        for(String field: entityKeyList) {
            tmpFieldsMap.remove(field);
        }

        // Obteniendo listado de campos del get no presentes en el getfields
        for(String field: fieldListHashMap.keySet()) {
            extraFieldList.remove(field);
        }

        // Eliminando campos del getfields no devueltos en el get en la lista de campos de salida
        for(String field: tmpFieldsMap.keySet()) {
            fieldListHashMap.remove(field);
        }

        // Añadiendo nuevos campos devueltos en el get y no presentes en el getFields
        // en la lista de campos de salida y en la lista de filtros
        for(String fieldName: extraFieldList) {
            CiviField civiField = new CiviField();
            civiField.setName(fieldName);
            civiField.setFieldName(fieldName);
            civiField.setTitle("");
            civiField.setSource("get");
            if (fieldName.endsWith("_id"))
                civiField.setType(CiviField.dataTypeMapping.get("Integer"));
            else
                civiField.setType(CiviField.dataTypeMapping.get("String"));

            fieldListHashMap.put(fieldName, civiField);
            filterFieldHashMap.put(fieldName, civiField);
        }
    }

    public ArrayList<String> getEntityActions(boolean input) throws IOException, CiviCRMException {
        ArrayList<String> actions = new ArrayList<String>();
        try {
            String params = "&options[limit]=0";

            URL url = getUrl(this.entity, this.action, params);
            HttpURLConnection conn = getHttpConnection("GET", url);
            String json = getCiviResponse(conn);

            JSONObject jsonObject = (JSONObject) parser.parse(json);

            // Aqui debo chequear si hay un error de acceso
            if ((Long)jsonObject.get("is_error") == 1L) {
                throw new CiviCRMException("CiviCRM API Error: " + jsonObject.get("error_message").toString());
            } else if ((Long)jsonObject.get("is_error") == 0L && (Long)jsonObject.get("count") == 0L) {
                throw new CiviCRMException("CiviCRM Error: Entity [" + this.entity + "] has not fields or not exists ");
            } else {
                JSONArray values = (JSONArray) jsonObject.get("values");
                actions.addAll(values.subList(0, values.size()));

                if (input) {
                    actions.removeAll(this.noInputActions);
                    if (!actions.contains("get")) {
                        actions.add(0, "get");
                    }
                } else {
                    actions.removeAll(this.noOutputActions);
                }
                HashSet<String> set = new HashSet<String>(actions);
                String stringArray[] = new String[0];
                stringArray = set.toArray(stringArray);
                Arrays.sort(stringArray);
                actions.clear();
                actions.addAll(Arrays.asList(stringArray));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        }
        return actions;
    }

    public ArrayList<String> getEntityList() throws IOException, ParseException, CiviCRMException {
        return getEntityList(true);
    }

    public ArrayList<String> getEntityList(boolean refresh) throws IOException, ParseException, CiviCRMException {
        if (entityList.size() == 0 || refresh) {
            entityList = new ArrayList<String>();
            URL url = getUrl("Entity", "get", "");
            HttpURLConnection conn = getHttpConnection("GET", url);
            String jsonString = getCiviResponse(conn);

            JSONObject jsonObject = (JSONObject) parser.parse(jsonString);

            // Aqui debo chequear si hay un error de acceso
            if ((Long) jsonObject.get("is_error") == 1L) {
                throw new CiviCRMException("CiviCRM API Error: " + jsonObject.get("error_message").toString());
            } else {
                JSONArray values = (JSONArray) jsonObject.get("values");

                Iterator<String> valueIterator = values.iterator();
                String entityName;
                // take each value from the json array separately
                while (valueIterator.hasNext()) {
                    entityName = valueIterator.next();
                    entityList.add(entityName);
                }
            }
        }
        return entityList;
    }

    public Long getRowCount(String params) throws IOException, CiviCRMException, ParseException {
        if (!this.isFirst && (this.action.equals("getoptions") || this.action.equals("getvalue") || this.action.equals("getsingle"))) {
            return 0L;
        } else if (this.isFirst) {
            if (this.action.equals("getoptions") || this.action.equals("getvalue") || this.action.equals("getsingle")) {
                return 1L;
            } else {
                URL url = getUrl(entity, "getcount", params);
                HttpURLConnection conn = getHttpConnection("GET", url);
                this.callUrl = conn.getRequestMethod() + ": " + url.toString();

                String jsonResponse = getCiviResponse(conn);
                JSONObject jsonObject = (JSONObject) parser.parse(jsonResponse);

                // Aqui debo chequear si hay un error de acceso
                if ((Long) jsonObject.get("is_error") == 1L) {
                    throw new CiviCRMException("CiviCRM API Error: " + jsonObject.get("error_message").toString());
                } else {
                    this.entityCount = Long.valueOf(jsonObject.get("result").toString());
                }
            }
        }
        return this.entityCount;
    }

    public List<HashMap<String, Object>> getNextValues() throws IOException, CiviCRMException, ParseException {
        this.offset = (this.isFirst ? 0 : this.offset + this.limit);
        ArrayList<HashMap<String, Object>> returnValueList = new ArrayList<HashMap<String, Object>>();
        String params = (this.options.equals("") ? "" : this.options);
        if (this.offset >= getRowCount(params)) {
            return returnValueList;
        }
        this.isFirst = false;

        if (this.action.equals("getsingle") || this.action.equals("getvalue") || this.action.equals("getcount") || this.action.equals("getoptions")) {
            if (this.offset > 1) {
                // No hacer la llamada si la accion es getsingle pues se devuelve un solo registro
                // en la primera llamada,en las siguientes genera un error
                return returnValueList;
            }
        }

        params += "&options[limit]=" + this.limit
                + "&options[offset]=" + this.offset
                + (isDebug ? "&debug=1" : "");

        URL url = getUrl(entity, action, params);
        HttpURLConnection conn = getHttpConnection("GET", url);
        this.callUrl = conn.getRequestMethod() + ": " + url.toString();

        String jsonResponse = getCiviResponse(conn);

        JSONObject jsonObject = (JSONObject)parser.parse(jsonResponse);

        // Aqui debo chequear si hay un error de acceso
        if ((Long)jsonObject.get("is_error") == 1L) {
            this.isError = true;
            this.civiJsonError = jsonResponse;
            if (returnEmptyOnFail)
                return returnValueList;
            else
                throw new CiviCRMException("CiviCRM API Error: " + jsonObject.get("error_message").toString());
        } else {
            isError = false;
            this.civiJsonError = "";
                    Object valueField = jsonObject.get("values");
            Object fieldValue;

            if (valueField == null) {
                // getsingle, getvalue, getcount
                // {"key1":"Value1","key2":"Value2", ...}

                HashMap<String, Object> fieldValues = new HashMap<String, Object>();
                for (Object fieldName: jsonObject.keySet()) {
                    if (!fieldName.equals("is_error")) {
                        fieldValue = jsonObject.get(fieldName);
                        fieldValues.put((String) fieldName, fieldValue);
                    }
                }
                returnValueList.add(fieldValues);
            } else if (action.equals("getoptions")) {
                // getoptions
                // {"key1":"Value1","key2":"Value2", ...}
                JSONObject fieldSet = (JSONObject) valueField;

                for (Object fieldName: fieldSet.keySet()) {
                    HashMap<String, Object> fieldValues = new HashMap<String, Object>();
                    fieldValue = fieldSet.get(fieldName);
                    fieldValues.put("key", fieldName);
                    fieldValues.put("value", fieldValue);
                    returnValueList.add(fieldValues);
                }
            } else if(valueField.getClass().getSimpleName().equals("JSONObject")) {
                JSONObject fieldSet = (JSONObject) valueField;
                //boolean is_rowset = false;
                for (Object key: fieldSet.keySet()) {
                    HashMap<String, Object> fieldValues = new HashMap<String, Object>();
                    Object field = fieldSet.get(key);
                    if (field.getClass().getSimpleName().equals("JSONObject")) {
                        // MailSettings
                        // {"object1":{"key1":"Value1","key2":"Value2", ...},"object2":{"key1":"Value1","key2":"Value2", ...},...}
                        JSONObject jsonField = (JSONObject) field;
                        for (Object fieldName: jsonField.keySet()) {
                            fieldValue = jsonField.get(fieldName);
                            fieldValues.put((String)fieldName, fieldValue);
                        }

                    } else if (field.getClass().getSimpleName().equals("String")) {
                        // ActivityType
                        // {"id1":"Value1","id2":"Value2", ...}
                        fieldValue = field;
                        fieldValues.put("ActivityTypeId", key); //(String)key
                        fieldValues.put(this.entity, fieldValue); //(String)key
                    }
                    returnValueList.add(fieldValues);
                }
            } else if (valueField.getClass().getSimpleName().equals("JSONArray")) {
                JSONArray values = (JSONArray)jsonObject.get("values");

                Iterator<Object> iterator = values.iterator();

                while (iterator.hasNext()) {
                    Object entityObject = iterator.next();
                    HashMap<String, Object> fieldValues = new HashMap<String, Object>();

                    if (entityObject.getClass().getSimpleName().equals("JSONObject")) {
                        // File
                        // [{"key1":"Value1","key2":"Value2", ...},{"key1":"Value1","key2":"Value2", ...},...]
                        JSONObject jsonArrayField = (JSONObject) entityObject;
                        for (Object fieldName: jsonArrayField.keySet()) {
                            fieldValue = jsonArrayField.get(fieldName);
                            fieldValues.put((String)fieldName, fieldValue);
                        }
                    } else if (entityObject.getClass().getSimpleName().equals("String")) {
                        // Entity
                        // ["Activity","ActivityType","Address", ...]
                        fieldValue = entityObject;
                        fieldValues.put((String)key, fieldValue);
                    }
                    returnValueList.add(fieldValues);
                }
            }
        }
        return returnValueList;
    }

    public String[] preparePostUrlString(String entity, String action, String params) {
        String[] preparedUrl = new String[2];

        preparedUrl[0] = restUrl;

        preparedUrl[1] = "api_key=".concat(apiKey);
        preparedUrl[1] = preparedUrl[1] + "&key=".concat(key);

        preparedUrl[1] = preparedUrl[1].concat("&json=1");
        preparedUrl[1] = preparedUrl[1].concat("&debug=1");
        preparedUrl[1] = preparedUrl[1].concat("&version=3");

        preparedUrl[1] = preparedUrl[1] + "&entity=".concat(entity);
        preparedUrl[1] = preparedUrl[1] + "&action=".concat(action);

        preparedUrl[1] = preparedUrl[1] + params;

        return preparedUrl;
    }

    public String putValues(String params, String filters, boolean closeConnection) {
        boolean error = false;
        String msg = "";
        try {
            // Ver lor filtros para la actualizacion
            String apiUrlString[] = preparePostUrlString(entity, action, params);
      /*
       * Estos dos metodos hacen lo mismo, depende del gusto el que se use, creo
       * que es mejor usando socket pues es mas simple tener la traza completa
       * para depurar y mantener la conexion abierta hasta finalizar
       */

            msg = tryPostWithUrl(apiUrlString);
            // String result = tryPostWithSocket(apiUrlString, closeConnection);
            // Si aparece la cadena "error_code" se ha producido un error, hay que
            // verificar otros
            // casos con la variable is_error

        } catch (Exception e) {
            e.printStackTrace();
        }
        return msg;
    }

    public void closeSocketConnection() {
        try {
            if (socketReader != null) {
                socketReader.close();
            }
            if (sockedWriter != null) {
                sockedWriter.close();
            }
            if (civiSocket != null && !civiSocket.isClosed()) {
                civiSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String tryPostWithSocket(String params[], boolean closeConnection) {
        String postResult = "";
        try {
            // Construct data
            String data = params[1];
            URL url = new URL(params[0]);

            // Create a socket to the host
            String hostname = url.getHost();
            int port = url.getPort() == -1 ? 80 : url.getPort();
            InetAddress addr = InetAddress.getByName(hostname);
            if (!closeConnection && (civiSocket == null || civiSocket.isClosed())) {
                civiSocket = new Socket(addr, port);
            }

            // Send header
            String path = url.getPath();
            if (!closeConnection && sockedWriter == null) {
                sockedWriter = new BufferedWriter(new OutputStreamWriter(civiSocket.getOutputStream(), "UTF8"));
            }

            sockedWriter.write("POST " + path + " HTTP/1.0\r\n");
            sockedWriter.write("Content-Length: " + data.length() + "\r\n");
            sockedWriter.write("Content-Type: application/x-www-form-urlencoded\r\n");
            sockedWriter.write("\r\n");

            sockedWriter.write(data);
            sockedWriter.flush();

            // Get the response
            if (!closeConnection && socketReader == null) {
                socketReader = new BufferedReader(new InputStreamReader(civiSocket.getInputStream()));
            }
            String line;
            while ((line = socketReader.readLine()) != null) {
                postResult += line + "\n";
            }

            if (closeConnection) {
                sockedWriter.close();
                socketReader.close();
                civiSocket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return postResult;
    }

    public String tryPostWithUrl(String params[]) {
        String postResult = "";
        try {
            // Construct data
            String data = params[1];
            URL url = new URL(params[0]);

            data = data + (isDebug ? "&debug=1" : "");
            this.callUrl = "POST: " + url.toString() + "\nSend data: " + data;

            // Send data
            URLConnection conn = url.openConnection();
            conn.setDoOutput(true);
            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());

            wr.write(data);
            wr.flush();

            // Get the response
            BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = rd.readLine()) != null) {
                postResult += line + "\n";
            }
            wr.close();
            rd.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return postResult;
    }

    public void setDebug(boolean isDebug) {
        this.isDebug = isDebug;
    }

    public String getCallUrl() {
        return callUrl;
    }

    public void setCallUrl(String callUrl) {
        this.callUrl = callUrl;
    }

    public boolean getIsDebug() {
        return isDebug;
    }

    public String getCiviJsonError() {
        return civiJsonError;
    }

    public void setReturnEmptyOnFail(Boolean returnEmptyOnFail) {
        this.returnEmptyOnFail = returnEmptyOnFail;
    }

    public Boolean getReturnEmptyOnFail() {
        return returnEmptyOnFail;
    }
}