/*
 * DefaultApplicationContextProxy.java  $Revision: 1.5 $  $Date: 2007/10/03 04:39:40 $
 *
 * Copyright (C) 2007 Meldcraft Corporation
 *
 * This file is part of Scaffold.
 * 
 * Scaffold is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Scaffold is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Scaffold.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.meldcraft.application.util;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.meldcraft.application.ApplicationElementContext;
import com.meldcraft.application.ReflectInvoker;
import com.meldcraft.util.PropertiesHelper;

/**
 * @version $Revision: 1.5 $ $Date: 2007/10/03 04:39:40 $
 * @author Rob Richards
 */
public class DefaultApplicationContextProxy implements InvocationHandler
{
    protected ApplicationElementContext mElementContext;
    protected Map                       mMethodProperties = new HashMap();
    protected long                      mThrottleMillis = 0;
    protected String                    mOnThreadInterrupt = null;

    protected DefaultApplicationContextProxy(ApplicationElementContext elementContext)
    {
        mElementContext = elementContext;

        String[] props = PropertiesHelper.getStringArray(mElementContext.getConfiguration(), "interfaceMethodProperties");
        if (props != null)
        {
            for (int i=0 ; i < props.length ; i++)
            {
                String str = props[i];
                String method = str;
                String prop = "";

                if (method.startsWith(ReflectInvoker.CONFIG_THROTTLE_KEY))
                {
                    int idx = method.indexOf('=');
                    if ((idx > 0) && (idx < method.length()-1))
                    {
                        mThrottleMillis = PropertiesHelper.getLong(method.substring(idx+1).trim(), 0);
                    }
                }
                else if (method.startsWith("onThreadInterrupt"))
                {
                    int idx = method.indexOf('=');
                    if ((idx > 0) && (idx < method.length()-1))
                    {
                        mOnThreadInterrupt = method.substring(idx+1);
                    }
                }
                else
                {
                    int idx;
                    if ((idx = str.indexOf('.')) != -1)
                    {
                        method = str.substring(0, idx);
                        prop = (str.length() > idx+1) ? str.substring(idx+1) : "";
                    }

                    List list = (List)mMethodProperties.get(method);
                    if (list == null)
                    {
                        list = new ArrayList();
                        mMethodProperties.put(method, list);
                    }

                    if (!list.contains(prop))
                    {
                        list.add(prop);
                    }
                }
            }
        }
    }

    public static Object createInstance(ApplicationElementContext elementContext)
        throws ClassNotFoundException
    {
        Object ret = null;

        Class[] classes = null;

        Map config = elementContext.getConfiguration();
        String[] interfaces = PropertiesHelper.getStringArray(config, "interfaces");
        if (interfaces != null)
        {
            classes = new Class[interfaces.length];
            for (int i=0 ; i < interfaces.length ; i++)
            {
                classes[i] = Class.forName(interfaces[i]);
            }
        }

        if (classes != null)
        {
            ret = Proxy.newProxyInstance(classes[0].getClassLoader(), classes, new DefaultApplicationContextProxy(elementContext));
        }

        return ret;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        if (mOnThreadInterrupt != null)
        {
            if (Thread.currentThread().interrupted())
            {
                BeanUtil.translateValue(new BeanContext(mElementContext.getApplicationContext(), this, args, mOnThreadInterrupt));
            }
        }

        boolean doit = true;

        Object val = args;
        if (mMethodProperties.size() > 0)
        {
            List list = (List)mMethodProperties.get(method.getName());
            if (list == null)
            {
                doit = false;
            }
            else
            {
                HashMap map = new HashMap();
                map.put("args", args);
                Object[] vals = new Object[list.size()];
                for (int i=0 ; i < vals.length ; i++)
                {
                    String str = (String)list.get(0);
                    if (str.length() == 0)
                    {
                        vals[i] = args;
                    }
                    else
                    {
                        vals[i] = BeanUtil.getPropertyValue(map, str);
                    }
                }
                val = (vals.length == 1) ? vals[0] : vals;
            }
        }
        else if ((args != null) && (args.length == 1))
        {
            val = args[0];
        }

        if (doit)
        {
            mElementContext.getApplicationContext().contextChanged(mElementContext.getElementId()+"."+method.getName(), val);
            if (mThrottleMillis > 0)
            {
                try
                {
                    Thread.sleep(mThrottleMillis);
                }
                catch (Exception e)
                {
                    // ignored
                }
            }
        }
        return null;
    }
}



