/**
 * Copyright (c) 2006 - 2010 Smaxe Ltd (www.smaxe.com).
 * All rights reserved.
 */
import com.smaxe.uv.ObjectEncoding;
import com.smaxe.uv.Responder;
import com.smaxe.uv.amf.ClassObject;
import com.smaxe.uv.amf.IObjectCreator;
import com.smaxe.uv.amf.support.ObjectCreator;
import com.smaxe.uv.amf3.ArrayCollection;
import com.smaxe.uv.client.License;
import com.smaxe.uv.remoting.ds.AmfConnection;
import com.smaxe.uv.remoting.ds.RemotingResponder;
import com.smaxe.uv.remoting.ds.messages.AbstractMessage;
import com.smaxe.uv.remoting.ds.messages.RemotingMessage;

import java.io.Externalizable;
import java.util.HashMap;
import java.util.Map;

/**
 * <code>ExAmfConnection</code> - {@link AmfConnection} usage example.
 * <p> Note:
 * <br> The example is based on Samples applications provided with BlazeDS server.
 * 
 * @author Andrei Sochirca
 */
public final class ExAmfConnection extends Object
{
    /**
     * Entry point.
     * 
     * @param args
     * @throws Exception if an exception occurred
     */
    public static void main(final String[] args) throws Exception
    {
        // NOTE:
        // you can get Evaluation Key at:
        // http://www.smaxe.com/order.jsf#request_evaluation_key
        // or buy at:
        // http://www.smaxe.com/order.jsf
        License.setKey("SET-YOUR-KEY");
        
        final String url = "http://localhost:8400/samples/messagebroker/amf";
        
        
        Map<String, Object> configuration = new HashMap<String, Object>();
        
//        configuration.put(AmfConnection.Configuration.USE_HTTP_URL_CONNECTION, false);
        configuration.put(AmfConnection.Configuration.OBJECT_CREATOR, new CustomObjectCreator());
        
        AmfConnection connection = new AmfConnection(configuration);
        
        connection.objectEncoding(ObjectEncoding.AMF3);
        
        connection.addEventListener(new AmfConnection.IListener()
        {
            public void onIOError(AmfConnection connection, final String message)
            {
                System.out.println("AmfConnection$IListener#onIOError: " + connection.uri() + " " + message);
            }
            
            public void onConnect(AmfConnection connection, final String id)
            {
                System.out.println("AmfConnection$IListener#onConnect: " + connection.uri() + " " + id);
            }
            
            public void onDisconnect(AmfConnection connection)
            {
                disconnected = true;
                
                System.out.println("AmfConnection$IListener#onDisconnect: " + connection.uri());
            }
        });
        
        connection.connect(url);
        
        // wait till connected
        while (!connection.connected() && !disconnected)
        {
            try
            {
                Thread.sleep(100);
            }
            catch (Exception e) {/*ignore*/}
        }
        
        if (!disconnected)
        {
            connection.call("my-amf" /*endpoint*/, "runtime-employee-ro" /*destination*/,
                    "getEmployees", new Responder()
            {
                public void onResult(final Object result)
                {
                    System.out.println("getEmployees() result: " + result);
                    
                    if (result instanceof ArrayCollection)
                    {
                        for (Object o : ((ArrayCollection) result).array)
                        {
                            System.out.println(o);
                        }
                    }
                }
                
                public void onStatus(final Map<String, Object> status)
                {
                    System.out.println("getEmployees() status: " + status);
                }
            });
            
            
            // other way to call remote method is using RemotingMessage instance
            
            RemotingMessage call = new RemotingMessage();
            
            call.messageId = AmfConnection.createUUID();
            call.destination = "runtime-employee-ro";
            call.operation = "getEmployees";
            call.body = new Object[0]; // getEmployees() arguments
            
            call.headers.put(RemotingMessage.ENDPOINT_HEADER, "my-amf");
            // NOTE:
            // call.headers.put(RemotingMessage.FLEX_CLIENT_ID_HEADER, <id>);
            // this ID is added by AmfConnection class before sending to the server 
            
            connection.sendMessage(call, new RemotingResponder()
            {
                public void onMessage(final AbstractMessage message)
                {
                    System.out.println("getEmployees() message: " + message);
                }
                
                // Responder implementation
                
                public void onResult(final Object result)
                {
                    System.out.println("getEmployees() result: " + result);
                    
                    if (result instanceof ArrayCollection)
                    {
                        for (Object o : ((ArrayCollection) result).array)
                        {
                            System.out.println(o);
                        }
                    }
                }
                
                public void onStatus(final Map<String, Object> status)
                {
                    System.out.println("getEmployees() status: " + status);
                }
            });
        }
        
        try
        {
            Thread.sleep(2 * 1000);
        }
        catch (Exception e) {/*ignore*/}
        
        connection.close();
    }
    
    private static boolean disconnected = false;
    
    /**
     * <code>Employee</code> - employee class.
     */
    public static class Employee extends Object
    {
        // fields
        /**
         * employee id.
         */
        public int employeeId;
        /**
         * first name.
         */
        public String firstName;
        /**
         * last name.
         */
        public String lastName;
        /**
         * company.
         */
        public Company company;
        /**
         * title.
         */
        public String title;
        /**
         * phone number.
         */
        public String phone;
        /**
         * email address.
         */
        public String email;
        
        /**
         * Constructor.
         */
        public Employee()
        {
        }
        
        @Override
        public String toString()
        {
            return "Employee [" +
                "employeeId=" + employeeId + ", " +
                "firstName=" + firstName + ", " +
                "lastName=" + lastName + ", " +
                "company=" + company + ", " +
                "title=" + title + ", " +
                "phone=" + phone + ", " +
                "email=" + email + 
                "]";
        }
    }
    
    /**
     * <code>Company</code> - company class.
     */
    public static class Company extends Object
    {
        // fields
        /**
         * company id.
         */
        public int companyId;
        /**
         * company name.
         */
        public String name;
        /**
         * industry.
         */
        public String industry;
        /**
         * state.
         */
        public String state;
        /**
         * city.
         */
        public String city;
        /**
         * zip code.
         */
        public String zip;
        /**
         * address.
         */
        public String address;
        
        /**
         * Constructor.
         */
        public Company()
        {
        }
        
        @Override
        public String toString()
        {
            return "Company [" +
                "companyId=" + companyId + ", " +
                "name=" + name + ", " +
                "industry=" + industry + ", " +
                "state=" + state + ", " +
                "city=" + city + ", " +
                "zip=" + zip + ", " +
                "address=" + address + 
                "]";
        }
    }
    
    /**
     * <code>CustomObjectCreator</code> - custom {@link IObjectCreator} implementation.
     */
    public static class CustomObjectCreator extends Object implements IObjectCreator
    {
        // fields
        private IObjectCreator defaultObjectCreator = new ObjectCreator();
        
        /**
         * Constructor.
         */
        public CustomObjectCreator()
        {
        }
        
        // IObjectCreator implementation
        
        public ClassObject toClassObject(Object o)
        {
            ClassObject co = defaultObjectCreator.toClassObject(o);
            
            if (o instanceof Employee)
            {
                co.className = "flex.samples.crm.employee.Employee";
            }
            else
            if (o instanceof Company)
            {
                co.className = "flex.samples.crm.company.Company";
            }
            
            return co;
        }
        
        public Object toObject(ClassObject co)
        {
            if ("flex.samples.crm.employee.Employee".equals(co.className))
            {
                co.className = ExAmfConnection.class.getCanonicalName() + "$" + Employee.class.getSimpleName();
            }
            else
            if ("flex.samples.crm.company.Company".equals(co.className))
            {
                co.className = ExAmfConnection.class.getCanonicalName() + "$" + Company.class.getSimpleName();
            }
            
            return defaultObjectCreator.toObject(co);
        }
        
        public String getClassName(Externalizable e)
        {
            return defaultObjectCreator.getClassName(e);
        }
        
        public Externalizable getExternalizable(String className)
        {
            return defaultObjectCreator.getExternalizable(className);
        }
    }
}