package com.poiji.internal.marshaller;

import com.poiji.exception.IllegalCastException;
import com.poiji.exception.PoijiInstantiationException;
import com.poiji.internal.PoiWorkbook;
import com.poiji.internal.PoijiOptions;
import com.poiji.internal.annotation.ExcelCell;
import com.poiji.util.Casting;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * This is the main class that converts the excel sheet fromExcel Java object
 * <br>
 * Created by hakan on 16/01/2017.
 */
final class Unmarshaller implements Deserializer {

    private final PoiWorkbook poiWorkbook;
    private final DataFormatter df = new DataFormatter();

    Unmarshaller(final PoiWorkbook poiWorkbook) {
        this.poiWorkbook = poiWorkbook;
    }

    public <T> List<T> deserialize(Class<T> type, PoijiOptions options) {
        Sheet sheet = poiWorkbook.workbook().getSheetAt(0);
        int skip = options.skip();
        int maxPhysicalNumberOfRows = sheet.getPhysicalNumberOfRows() + 1 - skip;
        List<T> list = new ArrayList<>(maxPhysicalNumberOfRows);

        for (Row currentRow : sheet) {

            if (skip(currentRow, skip))
                continue;

            if (maxPhysicalNumberOfRows > list.size()) {
                T t = deserialize0(currentRow, type);
                list.add(t);
            }
        }

        return list;
    }

    private <T> T deserialize0(Row currentRow, Class<T> type) {

        T instance;
        try {
            instance = type.newInstance();
        } catch (IllegalAccessException | InstantiationException e) {
            throw new PoijiInstantiationException("Cannot create a new instance of " + type.getName());
        }

        return setFieldValue(currentRow, type, instance);

    }

    private <T> T tailSetFieldValue(Row currentRow, Class<? super T> type, T instance) {
        for (Field field : type.getDeclaredFields()) {

            ExcelCell index = field.getAnnotation(ExcelCell.class);
            if (index != null) {
                Class<?> fieldType = field.getType();
                Cell cell = currentRow.getCell(index.value());
                Object o;

                if (!field.isAccessible())
                    field.setAccessible(true);

                if (cell != null) {
                    String value = df.formatCellValue(cell);
                    o = castValue(fieldType, value);
                } else {
                    o = castValue(fieldType, "");
                }
                try {
                    field.set(instance, o);
                } catch (IllegalAccessException e) {
                    throw new IllegalCastException("Unexpected cast type {" + o + "} of field" + field.getName());
                }
            }
        }
        return instance;
    }

    private <T> T setFieldValue(Row currentRow, Class<? super T> subclass, T instance) {
        return subclass == null
                ? instance
                : tailSetFieldValue(currentRow, subclass, setFieldValue(currentRow, subclass.getSuperclass(), instance));
    }

    private Object castValue(Class<?> fieldType, String value) {
        try {
            Object o;
            if (fieldType.getName().equals("int")) {
                o = Casting.integerValue(Objects.equals(value, "") ? "0" : value);

            } else if (fieldType.getName().equals("long")) {
                o = Casting.longValue(Objects.equals(value, "") ? "0" : value);

            } else if (fieldType.getName().equals("double")) {
                o = Casting.doubleValue(Objects.equals(value, "") ? "0" : value);

            } else if (fieldType.getName().equals("float")) {
                o = Casting.floatValue(Objects.equals(value, "") ? "0" : value);

            } else if (fieldType.getName().equals("boolean")) {
                o = Boolean.valueOf(value);

            } else if (fieldType.getName().equals("byte")) {
                o = Casting.byteValue(Objects.equals(value, "") ? "0" : value);

            } else if (fieldType.getName().equals("short")) {
                o = Casting.shortValue(Objects.equals(value, "") ? "0" : value);

            } else if (fieldType.getName().equals("char")) {
                value = Objects.equals(value, "") ? " " : value;
                o = value.charAt(0);

            } else
                o = value;
            return o;
        } catch (Exception e) {
            throw new IllegalCastException("Unexpected cast value {" + value + "} of type " + fieldType.getName());
        }
    }

    private boolean skip(final Row currentRow, int skip) {
        return currentRow.getRowNum() + 1 <= skip;
    }
}

@@ -3344,34 +3344,16 @@
 alue) %7B%0A
-        try %7B%0A    
         
@@ -3358,28 +3358,24 @@
   Object o;%0A
-    
         if (
@@ -3415,36 +3415,32 @@
 ) %7B%0A            
-  
-  
 o = Casting.inte
@@ -3491,36 +3491,32 @@
 alue);%0A%0A        
-    
 %7D else if (field
@@ -3552,36 +3552,32 @@
 ) %7B%0A            
-  
-  
 o = Casting.long
@@ -3625,36 +3625,32 @@
 alue);%0A%0A        
-    
 %7D else if (field
@@ -3688,36 +3688,32 @@
 ) %7B%0A            
-  
-  
 o = Casting.doub
@@ -3763,36 +3763,32 @@
 alue);%0A%0A        
-    
 %7D else if (field
@@ -3825,36 +3825,32 @@
 ) %7B%0A            
-  
-  
 o = Casting.floa
@@ -3899,36 +3899,32 @@
 alue);%0A%0A        
-    
 %7D else if (field
@@ -3967,28 +3967,24 @@
             
-  
-  
 o = Boolean.
@@ -3992,36 +3992,32 @@
 alueOf(value);%0A%0A
-    
         %7D else i
@@ -4061,36 +4061,32 @@
 ) %7B%0A            
-  
-  
 o = Casting.byte
@@ -4134,36 +4134,32 @@
 alue);%0A%0A        
-    
 %7D else if (field
@@ -4196,36 +4196,32 @@
 ) %7B%0A            
-  
-  
 o = Casting.shor
@@ -4274,28 +4274,24 @@
 );%0A%0A        
-    
 %7D else if (f
@@ -4339,20 +4339,16 @@
         
-  
-  
 value = 
@@ -4392,36 +4392,32 @@
 ue;%0A            
-    
 o = value.charAt
@@ -4434,23 +4434,15 @@
     
-  
-  
 %7D else%0A
-    
     
@@ -4464,28 +4464,24 @@
 ue;%0A        
-  
-  
 return o;%0A  
@@ -4482,166 +4482,8 @@
  o;%0A
-        %7D catch (Exception e) %7B%0A            throw new IllegalCastException(%22Unexpected cast value %7B%22 + value + %22%7D of type %22 + fieldType.getName());%0A        %7D%0A
     

remove try catch