package com.alibaba.qlexpress4.runtime.instruction;

import com.alibaba.qlexpress4.QLOptions;
import com.alibaba.qlexpress4.exception.ErrorReporter;
import com.alibaba.qlexpress4.exception.MockErrorReporter;
import com.alibaba.qlexpress4.exception.QLRuntimeException;
import com.alibaba.qlexpress4.runtime.LeftValue;
import com.alibaba.qlexpress4.runtime.MetaClass;
import com.alibaba.qlexpress4.runtime.data.DataValue;
import com.alibaba.qlexpress4.test.property.Child;
import com.alibaba.qlexpress4.test.property.Parent;
import com.alibaba.qlexpress4.test.property.TestEnum;
import org.junit.Assert;
import org.junit.Test;

/**
 * Author: TaoKan
 */
public class GetFieldInstructionTest {
    /**
     * Parent::getStaticGet()  setMethod is null and fieldNotAccess
     */
    @Test
    public void case1() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "staticGet", false);
        MockQContextParent mockQContextParent = new MockQContextParent(false);
        mockQContextParent.push(new DataValue(new MetaClass(Parent.class)));
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        Assert.assertEquals((mockQContextParent.getValue()).get(), "staticGet1");
    }
    
    /**
     * public static case(Static field of Parent)
     * Parent.staticSet
     */
    @Test
    public void case2() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "staticSet", false);
        MockQContextParent mockQContextParent = new MockQContextParent(false);
        mockQContextParent.push(new DataValue(new MetaClass(Parent.class)));
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        ((LeftValue)mockQContextParent.getValue()).set("staticSet1", errorReporter);
        Assert.assertEquals((mockQContextParent.getValue()).get(), "staticSet1");
    }
    
    /**
     * private static case(Static field of Parent)
     * Parent.staticSetPrivate notSetAble
     */
    @Test
    public void case3() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "staticSetPrivate", false);
        MockQContextParent mockQContextParent = new MockQContextParent(false);
        mockQContextParent.push(new DataValue(new MetaClass(Parent.class)));
        try {
            getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        }
        catch (QLRuntimeException e) {
            Assert.assertEquals("FIELD_NOT_FOUND", e.getErrorCode());
            return;
        }
        Assert.fail();
    }
    
    /**
     * private static case(Static field of Parent)
     * Parent.staticSetPrivate allowPrivateAccess
     */
    @Test
    public void case4() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "staticSetPrivate", false);
        MockQContextParent mockQContextParent = new MockQContextParent(true);
        mockQContextParent.push(new DataValue(new MetaClass(Parent.class)));
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        Assert.assertEquals("staticSetPrivate", (mockQContextParent.getValue()).get());
    }
    
    /**
     * static case(Static method of Parent instance)
     * parent.getStaticGet() allowPrivateAccess
     */
    @Test
    public void case5() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "staticGet", false);
        MockQContextParent mockQContextParent = new MockQContextParent(true);
        mockQContextParent.push(new Parent());
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        Assert.assertEquals("staticGet1", (mockQContextParent.getValue()).get());
    }
    
    /**
     * normal case(member method of Parent instance)
     * parent.getAge()
     */
    @Test
    public void case6() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "age", false);
        MockQContextParent mockQContextParent = new MockQContextParent(false);
        mockQContextParent.push(new Parent());
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        ((LeftValue)mockQContextParent.getValue()).set(35, errorReporter);
        Assert.assertEquals((mockQContextParent.getValue()).get(), 35);
    }
    
    /**
     * normal case(member field of Parent instance)
     * parent.name allowPrivateAccess
     */
    @Test
    public void case7() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "name", false);
        MockQContextParent mockQContextParent = new MockQContextParent(true);
        mockQContextParent.push(new Parent());
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        ((LeftValue)mockQContextParent.getValue()).set("name1", errorReporter);
        Assert.assertEquals((mockQContextParent.getValue()).get(), "name1");
    }
    
    /**
     * error case(member field of Parent instance)
     * parent.name not accessible
     */
    @Test
    public void case8() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "name", false);
        MockQContextParent mockQContextParent = new MockQContextParent(false);
        mockQContextParent.push(new Parent());
        try {
            getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
            Assert.fail();
        }
        catch (QLRuntimeException e) {
            Assert.assertEquals("FIELD_NOT_FOUND", e.getErrorCode());
        }
    }
    
    /**
     * normal case(normal method of Parent,Child instance)
     * child.getAge() instead of parent.getAge()
     */
    @Test
    public void case9() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "age", false);
        MockQContextParent mockQContextParent = new MockQContextParent(true);
        mockQContextParent.push(new Child());
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        Assert.assertEquals((mockQContextParent.getValue()).get(), 11);
    }
    
    /**
     * normal case(member method of Parent,Child instance)
     * parent.getBirth() public instead of child.birth(not allow)
     */
    @Test
    public void case10() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "birth", false);
        MockQContextParent mockQContextParent = new MockQContextParent(false);
        mockQContextParent.push(new Child());
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        Assert.assertEquals((mockQContextParent.getValue()).get(), "2022-01-01");
    }
    
    /**
     * error case(member method of Parent instance)
     * parent.getMethod1() not found
     */
    @Test
    public void case11() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "method1", false);
        MockQContextParent mockQContextParent = new MockQContextParent(true);
        mockQContextParent.push(new Child());
        try {
            getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
            Assert.fail();
        }
        catch (QLRuntimeException e) {
            Assert.assertEquals("FIELD_NOT_FOUND", e.getErrorCode());
        }
    }
    
    /**
     * enum attr get
     * TestEnum.skt.getValue()
     */
    @Test
    public void case12() {
        ErrorReporter errorReporter = new MockErrorReporter();
        GetFieldInstruction getFieldInstruction = new GetFieldInstruction(errorReporter, "SKT", false);
        MockQContextParent mockQContextParent = new MockQContextParent(true);
        mockQContextParent.push(new DataValue(new MetaClass(TestEnum.class)));
        getFieldInstruction.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        GetFieldInstruction getFieldInstruction1 = new GetFieldInstruction(errorReporter, "value", false);
        getFieldInstruction1.execute(mockQContextParent, QLOptions.DEFAULT_OPTIONS);
        Assert.assertEquals((mockQContextParent.getValue()).get(), -1);
    }
    
}
