/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.usergrid.persistence.model.entity;


import java.io.IOException;
import java.util.*;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import org.apache.usergrid.persistence.model.field.*;
import org.apache.usergrid.persistence.model.field.value.EntityObject;

import com.fasterxml.jackson.databind.ObjectMapper;


/**
 * abstract conversion to Map<String,Object> form EntityObject
 */
public class EntityToMapConverter {
    public static final String LAT = "latitude";
    public static final String LON = "longitude";
    private final JsonFactory jsonFactory = new JsonFactory();
    private final ObjectMapper objectMapper = new ObjectMapper(jsonFactory).registerModule(new GuavaModule());

    /**
     * Convert Entity to Map, adding version_ug_field and a {name}_ug_analyzed field for each StringField.
     */



    /**
     * hacky impl, for outbound implementations longitude needs to be \ "longitude" and not "lon"
     * @param entityObject
     * @return
     */


    public EntityMap toMap( EntityObject entityObject ) {
        EntityMap map = new EntityMap();
        return toMap(entityObject, map);
    }

    private EntityMap toMap( EntityObject entity, EntityMap entityMap ) {
        for ( Field field : entity.getFields() ) {
            if ( field instanceof ListField || field instanceof ArrayField  || field instanceof SetField) {
                Collection list = ( Collection ) field.getValue();
                entityMap.put( field.getName(), processCollection( list )  );
            }
            else if ( field instanceof EntityObjectField ) {
                EntityObject eo = ( EntityObject ) field.getValue();
                entityMap.put( field.getName(), toMap( eo ) ); // recursion
            }
            else if ( field instanceof LocationField ) {
                LocationField locField = ( LocationField ) field;
                Map<String, Object> locMap = new HashMap<String, Object>();

                // field names lat and lon trigger ElasticSearch geo location
                locMap.put( LAT, locField.getValue().getLatitude() );
                locMap.put( LON, locField.getValue().getLongitude() );
                entityMap.put( field.getName(), locMap );
            }
            else if ( field instanceof ByteArrayField ) {

                ByteArrayField bf = ( ByteArrayField ) field;
                if( bf.getClassinfo().getName().contains("org.apache.usergrid.persistence.PathQuery") ){
                    //do not deserialize this contains Query and Query has changed
                    continue;
                }
                byte[] serilizedObj = bf.getValue();
                Object o;
                try {
                    o = objectMapper.readValue( serilizedObj, bf.getClassinfo() );
                }
                catch ( IOException e ) {
                    throw new RuntimeException( "Can't deserialize object from field:"
                        + field.getName()+ " classinfo: " + bf.getClassinfo()
                        + " byteArray of length:" + serilizedObj.length
                        , e );
                }
                entityMap.put( bf.getName(), o );
            }else if (field instanceof SerializedObjectField) {
                SerializedObjectField bf = (SerializedObjectField) field;

                String serilizedObj = bf.getValue();
                Object o;
                try {
                    o = objectMapper.readValue(serilizedObj, bf.getClassinfo());
                } catch (IOException e) {
                    throw new RuntimeException("Can't deserialize object " + serilizedObj, e);
                }
                entityMap.put(bf.getName(), o);
            } else {
                entityMap.put(field.getName(), field.getValue());
            }
        }

        return entityMap;
    }

    /**
     * Process the collection for our map
     * @param c
     * @return
     */
    private List<?> processCollection( Collection c ) {
        if ( c.isEmpty() ) {
            return Collections.emptyList();
        }

        List processed = new ArrayList(c.size());


        for(final Object element: c){
            processed.add( processCollectionElement( element ) );
        }

        return processed;
    }


    /**
     * Process each instance of data in our collection
     * @param element
     * @return
     */
    private Object processCollectionElement( final Object element ) {
        if ( element instanceof EntityObject ) {

            return toMap( ( EntityObject ) element );
        }

        //recurse into another list structure (2d + arrays)
        if (element instanceof ListField || element instanceof ArrayField  || element instanceof SetField){
            return processCollection( ( Collection ) ( ( AbstractField ) element ).getValue() );
        }

        if ( element instanceof List || element instanceof Set ) {

            return processCollection( ( Collection ) element ); // recursion;
        }

        return element;
    }
}

@@ -1570,16 +1570,99 @@
 dule());
+%0A    private static final Map%3CString,Boolean%3E corruptedTypes = getCorruptedTypes();
 %0A%0A    /*
@@ -1937,18 +1937,16 @@
      */%0A
-%0A%0A
     publ
@@ -3328,16 +3328,43 @@
     if( 
+corruptedTypes.containsKey(
 bf.getCl
@@ -3386,61 +3386,8 @@
 me()
-.contains(%22org.apache.usergrid.persistence.PathQuery%22
 ) )%7B
@@ -5450,19 +5450,17 @@
 n toMap(
- ( 
+(
 EntityOb
@@ -5463,17 +5463,16 @@
 tyObject
- 
 ) elemen
@@ -5472,17 +5472,16 @@
  element
- 
 );%0A     
@@ -5911,32 +5911,32 @@
 ion;%0A        %7D%0A%0A
-
         return e
@@ -5948,11 +5948,229 @@
 t;%0A    %7D
+%0A%0A    public static Map%3CString,Boolean%3E getCorruptedTypes() %7B%0A        Map%3CString,Boolean%3E typeMap = new HashMap%3C%3E() ;%0A        typeMap.put(%22org.apache.usergrid.persistence.PathQuery%22,true);%0A        return typeMap;%0A    %7D
 %0A%7D%0A

add map for bad types