package org.globsframework.model;

import org.globsframework.metamodel.Field;
import org.globsframework.metamodel.fields.FieldValueVisitor;

import java.util.Optional;

public interface FieldsValueScanner {
    static FieldsValueScanner from(FieldValue[] fieldValues) {
        return new FieldsValueScanner() {

            public <T extends FieldValues.Functor> T apply(T functor) throws Exception {
                for (FieldValue fieldValue : fieldValues) {
                    functor.process(fieldValue.getField(), fieldValue.getValue());
                }
                return functor;
            }

            public <T extends FieldValueVisitor> T accept(T functor) throws Exception {
                for (FieldValue fieldValue : fieldValues) {
                    fieldValue.getField().safeVisit(functor, fieldValue.getValue());
                }
                return functor;
            }
        };
    }

    <T extends FieldValues.Functor> T apply(T functor) throws Exception;

    default <T extends FieldValues.Functor> T safeApply(T functor) {
        try {
            return apply(functor);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    <T extends FieldValueVisitor> T accept(T functor) throws Exception;

    default <T extends FieldValueVisitor> T safeAccept(T functor) {
        try {
            return accept(functor);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    default FieldsValueScanner withoutKey() {
        if (this instanceof WithoutKeyFieldsValueScanner) {
            return this;
        }
        return new WithoutKeyFieldsValueScanner(this);
    }

    default int size() {
        return safeApply(new FieldValues.Functor() {
            int count;

            public void process(Field field, Object value) throws Exception {
                count++;
            }
        }).count;
    }

    class WithoutKeyFieldsValueScanner implements FieldsValueScanner {
        private FieldsValueScanner valueScanner;

        public WithoutKeyFieldsValueScanner(FieldsValueScanner valueScanner) {
            this.valueScanner = valueScanner;
        }

        public <T extends FieldValues.Functor> T apply(T functor) throws Exception {
            valueScanner.apply(functor.withoutKeyField());
            return functor;
        }

        public <T extends FieldValues.Functor> T safeApply(T functor) {
            valueScanner.safeApply(functor.withoutKeyField());
            return functor;
        }


        public <T extends FieldValueVisitor> T accept(T functor) throws Exception {
            valueScanner.accept(functor.withoutKey());
            return functor;
        }

        public <T extends FieldValueVisitor> T safeAccept(T functor) {
            valueScanner.safeAccept(functor.withoutKey());
            return functor;
        }
    }

    default Optional<Glob> toGlob(){
        return Optional.ofNullable(safeApply(new FieldValues.Functor() {
            MutableGlob glob;

            public void process(Field field, Object value) throws Exception {
                if (glob == null) {
                    glob = field.getGlobType().instantiate();
                }
                glob.setValue(field, value);
            }
        }).glob);
    }
}

@@ -3036,30 +3036,20 @@
 ult 
-Optional%3C
 Glob
-%3E
  toGlob(
 )%7B%0A 
@@ -3044,17 +3044,25 @@
  toGlob(
-)
+Key key) 
 %7B%0A      
@@ -3074,28 +3074,8 @@
 urn 
-Optional.ofNullable(
 safe
@@ -3155,109 +3155,112 @@
     
-public void process(Field field, Object value) throws Exception %7B%0A                if (glob == null) %7B
+%7B%0A                glob = key.getGlobType().instantiate();%0A                key.safeApplyOnKeyField(this);
 %0A   
@@ -3259,32 +3259,36 @@
 is);%0A           
+ %7D%0A%0A
          glob = 
@@ -3284,67 +3284,76 @@
     
-glob = field.getGlobType().instantiate();%0A                %7D
+   public void process(Field field, Object value) throws Exception %7B
 %0A   
@@ -3423,17 +3423,16 @@
  %7D).glob
-)
 ;%0A    %7D%0A

add Key to scanner to product a full glob.