package gov.va.med.edp.vistalink;

import gov.va.med.exception.FoundationsException;
import gov.va.med.vistalink.adapter.cci.VistaLinkConnection;
import gov.va.med.vistalink.adapter.cci.VistaLinkAppProxyConnectionSpec;
import gov.va.med.vistalink.adapter.cci.VistaLinkDuzConnectionSpec;
import gov.va.med.vistalink.rpc.*;
import junit.framework.TestCase;
import org.easymock.AbstractMatcher;
import org.easymock.ArgumentsMatcher;
import org.easymock.MockControl;
import org.springframework.util.FileCopyUtils;

import javax.resource.cci.ConnectionFactory;
import javax.resource.ResourceException;
import java.util.List;
import java.util.ArrayList;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;

public abstract class AbstractVistaLinkConnectionTest extends TestCase {

    protected MockControl mockConnectionFactoryControl;
    protected ConnectionFactory mockConnectionFactory;
    protected MockControl mockVistaLinkConnectionControl;
    protected VistaLinkConnection mockVistaLinkConnection;
    protected MockConnectionFactoryLocator mockConnectionFactoryLocator;

    static ArgumentsMatcher RPC_REQUEST_MATCHER = new RpcRequestMatcher();

    private int expectedTimeOut = VistaLinkTemplate.DEFAULT_TIMEOUT;

    protected String getResourceAsString(String resource) throws IOException {
        StringWriter w = new StringWriter();
        InputStreamReader r = new InputStreamReader(getClass().getResourceAsStream(resource));
        FileCopyUtils.copy(r, w);
        return w.toString();
    }

    protected void setUp() throws Exception {
        mockConnectionFactoryControl = MockControl.createControl(ConnectionFactory.class);
        mockConnectionFactory = (ConnectionFactory) mockConnectionFactoryControl.getMock();

        mockVistaLinkConnectionControl = MockControl.createControl(VistaLinkConnection.class);
        mockVistaLinkConnection = (VistaLinkConnection) mockVistaLinkConnectionControl.getMock();

        mockVistaLinkConnection.setTimeOut(getExpectedTimeOut());
        mockVistaLinkConnectionControl.setDefaultVoidCallable();

        mockConnectionFactoryLocator = new MockConnectionFactoryLocator();
        mockConnectionFactoryLocator.put(getStationNumber(), mockConnectionFactory);
    }

    protected int getExpectedTimeOut() {
        return expectedTimeOut;
    }

    protected void setExpectedTimeOut(int expectedTimeOut) {
        this.expectedTimeOut = expectedTimeOut;
    }


    protected abstract String getStationNumber();

    protected void expectVistaLinkDuzConnection(String duz) {
        try {
            mockConnectionFactoryControl.expectAndDefaultReturn(mockConnectionFactory.getConnection(new VistaLinkDuzConnectionSpec(getStationNumber(), duz)), mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }

    protected void expectVistaLinkAppProxyConnection(String appProxyName) {
        try {
            mockConnectionFactoryControl.expectAndDefaultReturn(mockConnectionFactory.getConnection(new VistaLinkAppProxyConnectionSpec(getStationNumber(), appProxyName)), mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }

    protected void expectRpcAndReturn(String rpcContext, String rpc, List params, String response) {
        try {
            RpcRequest request = RpcRequestFactory.getRpcRequest(rpcContext, rpc);
            if (params != null) request.setParams(params);
            expectRpcAndReturn(request, new TestRpcResponse(response));
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }

    protected void expectRpcAndReturn(RpcRequest request, RpcResponse response) throws FoundationsException {
        mockVistaLinkConnection.setTimeOut(getExpectedTimeOut());
        mockVistaLinkConnection.executeRPC(request);
        mockVistaLinkConnectionControl.setMatcher(RPC_REQUEST_MATCHER);
        mockVistaLinkConnectionControl.setReturnValue(response);
    }

    protected void expectRpcAndReturn(String rpcContext, String rpc, String response) {
        expectRpcAndReturn(rpcContext, rpc, null, response);
    }

    protected void expectRpcAndDefaultThrow(String rpcContext, String rpc, List params, Throwable t) {
        mockVistaLinkConnection.setTimeOut(getExpectedTimeOut());
        try {
            RpcRequest request = RpcRequestFactory.getRpcRequest(rpcContext, rpc);
            if (params != null) request.setParams(params);
            mockVistaLinkConnection.executeRPC(request);
            mockVistaLinkConnectionControl.setMatcher(RPC_REQUEST_MATCHER);
            mockVistaLinkConnectionControl.setDefaultThrowable(t);
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }


    protected void expectRpcAndDefaultThrow(String rpcContext, String rpc, Throwable t) {
        try {
            mockVistaLinkConnection.executeRPC(RpcRequestFactory.getRpcRequest(rpcContext, rpc));
            mockVistaLinkConnectionControl.setMatcher(RPC_REQUEST_MATCHER);
            mockVistaLinkConnectionControl.setDefaultThrowable(t);
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }

    protected void expectRpcAndReturnXmlResource(String rpcContext, String rpc, List params, String resource) throws IOException {
        try {
            RpcRequest request = RpcRequestFactory.getRpcRequest(rpcContext, rpc);
            if (params != null) request.setParams(params);
            RpcResponseFactory responseFactory = new RpcResponseFactory();
            RpcResponse response = (RpcResponse) responseFactory.handleResponse(getResourceAsString(resource), request);
            expectRpcAndReturn(request, response);
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }

    protected void replay() {
        mockConnectionFactoryControl.replay();
        mockVistaLinkConnectionControl.replay();
    }

    protected void verify() {
        mockConnectionFactoryControl.verify();
        mockVistaLinkConnectionControl.verify();
    }

    protected List createParams(Object arg1) {
        List l = new ArrayList();
        l.add(arg1);
        return l;
    }

    protected List createParams(Object arg1, Object arg2) {
        List l = new ArrayList();
        l.add(arg1);
        l.add(arg2);
        return l;
    }

    protected List createParams(Object arg1, Object arg2, Object arg3) {
        List l = new ArrayList();
        l.add(arg1);
        l.add(arg2);
        l.add(arg3);
        return l;
    }

    static String buildXmlResponse(String rpcResult) {
        StringBuffer buf = new StringBuffer();

        return buf.toString();
    }


    static class TestRpcResponse extends RpcResponse {
        protected TestRpcResponse(String rpcResult) {
            super(buildXmlResponse(rpcResult), buildXmlResponse(rpcResult), null, "gov.va.med.foundations.vistalink.response", rpcResult, "flee");
        }
    }

    static class RpcRequestMatcher extends AbstractMatcher {

        protected boolean argumentMatches(Object o1, Object o2) {
            RpcRequest r1 = (RpcRequest) o1;
            RpcRequest r2 = (RpcRequest) o2;

            return r1.getRpcName().equals(r2.getRpcName()) &&
                    r1.getRpcContext().equals(r2.getRpcContext()) &&
                    r1.getRpcClientTimeOut() == r2.getRpcClientTimeOut() &&
                    r1.getTimeOut() == r2.getTimeOut() &&
                    parametersMatch(r1.getParams(), r2.getParams());
        }

        private boolean parametersMatch(RpcRequestParams p1, RpcRequestParams p2) {
            if (p1 == null && p2 == null) return true;
            if (p1 != null && p2 == null) return false;
            if (p1 == null && p2 != null) return false;
            for (int pos = 1; true; pos++) {
                Object v1 = p1.getParam(pos);
                Object v2 = p2.getParam(pos);
                if (v1 == null && v2 == null) return true;
                if (v1 != null && v2 == null) return false;
                if (v1 == null && v2 != null) return false;
                return MockControl.EQUALS_MATCHER.matches(new Object[]{v1}, new Object[]{v2});
            }
        }
    }
}

/* this is the source once we switch to java 5 and later EasyMock
import gov.va.med.exception.FoundationsException;
import gov.va.med.vistalink.adapter.cci.VistaLinkAppProxyConnectionSpec;
import gov.va.med.vistalink.adapter.cci.VistaLinkConnection;
import gov.va.med.vistalink.adapter.cci.VistaLinkDuzConnectionSpec;
import gov.va.med.vistalink.vistalink.*;
import gov.va.med.edp.vistalink.VistaLinkTemplate;
import junit.framework.TestCase;
import org.easymock.EasyMock;
import org.easymock.IArgumentMatcher;
import org.easymock.internal.matchers.Equals;
import org.springframework.util.FileCopyUtils;

import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

public abstract class AbstractVistaLinkConnectionTest extends TestCase {

    protected ConnectionFactory mockConnectionFactory;
    protected VistaLinkConnection mockVistaLinkConnection;
    protected MockConnectionFactoryLocator mockConnectionFactoryLocator;

    private int expectedTimeOut = VistaLinkTemplate.DEFAULT_TIMEOUT;

    protected void setUp() throws Exception {
        mockConnectionFactory = EasyMock.createMock(ConnectionFactory.class);

        mockVistaLinkConnection = EasyMock.createMock(VistaLinkConnection.class);

        mockConnectionFactoryLocator = new MockConnectionFactoryLocator();
        mockConnectionFactoryLocator.put(getStationNumber(), mockConnectionFactory);
    }

    protected abstract String getStationNumber();

    protected String getResourceAsString(String resource) throws IOException {
        StringWriter w = new StringWriter();
        InputStreamReader r = new InputStreamReader(getClass().getResourceAsStream(resource));
        FileCopyUtils.copy(r, w);
        return w.toString();
    }

    protected int getExpectedTimeOut() {
        return expectedTimeOut;
    }

    protected void setExpectedTimeOut(int expectedTimeOut) {
        this.expectedTimeOut = expectedTimeOut;
    }

    protected void expectVistaLinkDuzConnection(String duz) {
        try {
            EasyMock.expect(mockConnectionFactory.getConnection(new VistaLinkDuzConnectionSpec(getStationNumber(), duz))).andReturn(mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }

    protected void expectVistaLinkAppProxyConnection(String appProxyName) {
        try {
            EasyMock.expect(mockConnectionFactory.getConnection(new VistaLinkAppProxyConnectionSpec(getStationNumber(), appProxyName))).andReturn(mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }

    protected void expectRpcAndReturn(String rpcContext, String vistalink, List params, String response) {
        try {
            RpcRequest request = RpcRequestFactory.getRpcRequest(rpcContext, vistalink);
            if (params != null) request.setParams(params);
            expectRpcAndReturn(request, new TestRpcResponse(response));
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e);
        }
    }

    protected void expectRpcAndReturn(RpcRequest request, RpcResponse response) throws FoundationsException {
        mockVistaLinkConnection.setTimeOut(getExpectedTimeOut());
        EasyMock.expect(mockVistaLinkConnection.executeRPC(eqRpcRequest(request))).andReturn(response);
    }

    protected void expectRpcAndReturnXmlResource(String rpcContext, String vistalink, List params, String resource) throws IOException {
        try {
            RpcRequest request = RpcRequestFactory.getRpcRequest(rpcContext, vistalink);
            if (params != null) request.setParams(params);
            RpcResponseFactory responseFactory = new RpcResponseFactory();
            RpcResponse response = (RpcResponse) responseFactory.handleResponse(getResourceAsString(resource), request);
            expectRpcAndReturn(request, response);
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e);
        }
    }

    protected void expectRpcAndDefaultThrow(String rpcContext, String vistalink, List params, Throwable t) {
        mockVistaLinkConnection.setTimeOut(getExpectedTimeOut());
        try {
            RpcRequest request = RpcRequestFactory.getRpcRequest(rpcContext, vistalink);
            if (params != null) request.setParams(params);
            EasyMock.expect(mockVistaLinkConnection.executeRPC(eqRpcRequest(request))).andThrow(t);
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e);
        }
    }

    protected void replay() {
        EasyMock.replay(mockConnectionFactory, mockVistaLinkConnection);
    }

    protected void verify() {
        EasyMock.verify(mockConnectionFactory, mockVistaLinkConnection);
    }

    static String buildXmlResponse(String rpcResult) {
        StringBuffer buf = new StringBuffer();

        return buf.toString();
    }

    protected List createParams(Object... args) {
        List l = new ArrayList();
        for (Object arg : args) {
            l.add(arg);
        }
        return l;
    }

    static class TestRpcResponse extends RpcResponse {
        protected TestRpcResponse(String rpcResult) {
            super(buildXmlResponse(rpcResult), buildXmlResponse(rpcResult), null, "gov.va.med.foundations.vistalink.response", rpcResult, "flee");
        }
    }

    static class RpcRequestEquals implements IArgumentMatcher {
        private RpcRequest expected;
        private RpcRequestParamsEquals paramMatcher;

        public RpcRequestEquals(RpcRequest expected) {
            this.expected = expected;
            this.paramMatcher = new RpcRequestParamsEquals(expected.getParams());
        }

        public boolean matches(Object request) {
            if (!(request instanceof RpcRequest)) {
                return false;
            }

            RpcRequest actual = (RpcRequest) request;
            return expected.getRpcName().equals(actual.getRpcName()) &&
                    expected.getRpcContext().equals(actual.getRpcContext()) &&
                    expected.getRpcClientTimeOut() == actual.getRpcClientTimeOut() &&
                    expected.getTimeOut() == actual.getTimeOut() &&
                    expected.isXmlResponse() == actual.isXmlResponse() &&
                    paramMatcher.matches(actual.getParams());
        }

        public void appendTo(StringBuffer buffer) {
            buffer.append("eqRpcRequest(");
            buffer.append(expected.toString());
            buffer.append("\")");
        }
    }

    static class RpcRequestParamsEquals implements IArgumentMatcher {
        private RpcRequestParams expected;

        public RpcRequestParamsEquals(RpcRequestParams expected) {
            this.expected = expected;
        }

        public boolean matches(Object request) {
            if (!(request instanceof RpcRequestParams)) {
                return false;
            }

            RpcRequestParams actual = (RpcRequestParams) request;

            int position = 1;
            Object expectedValue = expected.getParam(position);
            Object actualValue = expected.getParam(position);
            if (expectedValue == null && actualValue == null) return true;

            while (expectedValue != null && actualValue != null) {
                if (!(new Equals(expectedValue).matches(actualValue))) {
                    return false;
                }
                position++;
                expectedValue = expected.getParam(position);
                actualValue = actual.getParam(position);
            }
            if ((expectedValue == null && actualValue != null) || (expectedValue != null && actualValue == null))
                return false;
            return true;
        }

        public void appendTo(StringBuffer buffer) {
            buffer.append("eqRpcRequest(");
            buffer.append(expected.toString());
            buffer.append("\")");
        }
    }

    public static <T extends RpcRequest> T eqRpcRequest(T in) {
        EasyMock.reportMatcher(new RpcRequestEquals(in));
        return null;
    }
}
*/