<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
namespace com\fenqile\fsof\consumer\proxy;

use com\fenqile\fsof\common\url\FSOFUrl;
use com\fenqile\fsof\common\log\FSOFSystemUtil;
use com\fenqile\fsof\common\config\FSOFConstants;
use com\fenqile\fsof\common\config\FSOFCommonUtil;
use com\fenqile\fsof\registry\automatic\ConsumerProxy;
use com\fenqile\fsof\consumer\ConsumerException;


final class ProxyFactory
{
	/**
	 * @var  App加载的配置文件路径
	 */
	protected static $appConfigFile = '';

	/**
	 * @var  p2p模式开关
	 */
	protected static $p2pMode = FALSE;

    /**
     * @var app所属的group
     */
    protected static $appGroup = FSOFConstants::FSOF_SERVICE_GROUP_ANY;

    /**
     * @var app所属的version
     */
    protected static $appVersion = FSOFConstants::FSOF_SERVICE_VERSION_DEFAULT;
        
    /**
     * @var  appname
     */
    protected static $appName = 'consumer';

	/**
	 * @var  instance array
	 */
    protected static $serviceInstances = array();

    /**
	 * @var  *.consumer中配置的service信息
	 */
	protected static $serviceConsumers = array();

    /**
     * @var  *.consumer中配置的config信息
     */
	protected static $configConsumer = Array();

    private static $logger;

	public static function setConsumerConfig($configData, $consumerConfigFile, $initSettings)
	{
        self::$logger = \Logger::getLogger(__CLASS__);

		//App名字
		self::$appName = $initSettings['app_name'];

		//获取app加载配置文件;输出日志方便问题定位
		self::$appConfigFile = $consumerConfigFile;

		//是否开启p2p模式, p2p模式下, 将使用[consumer_services]下的信息进行路由
		if(isset($configData['consumer_config']['p2p_mode']) && $configData['consumer_config']['p2p_mode'])
		{
			self::$p2pMode = $configData['consumer_config']['p2p_mode'];
		}

		if (isset($configData['consumer_config']['group']))
		{
			self::$appGroup = $configData['consumer_config']['group'];
		}

		if(isset($configData['consumer_config']['version']))
		{
			self::$appVersion = $configData['consumer_config']['version'];
		}

        if(isset($configData['consumer_config']))
        {
            self::$configConsumer = $configData['consumer_config'];
        }

		if(isset($configData['consumer_services']))
		{
			self::$serviceConsumers = $configData['consumer_services'];
		}
	}

    //use automatic registry
    private static function getInstancByRedis($service, $ioTimeOut, $version, $group)
	{
        $ret = NULL;
        $providerInfo = ConsumerProxy::instance(self::$configConsumer)->getProviders($service, $version, $group);
        if(!empty($providerInfo))
        {
            $cacheKey = $service.':'.$version.':'.$group;
            if(empty(self::$serviceInstances[$cacheKey]))
            {
                $ret = Proxy::newProxyInstance($service,  self::$appName, $group);
                self::$serviceInstances[$cacheKey] = $ret;
            }
            else
            {
                $ret = self::$serviceInstances[$cacheKey];
            }

            //设置io超时时间
            $ret->setIOTimeOut($ioTimeOut);
			
            //设置地址列表
            $ret->setAddress($providerInfo);
        }
        else
        {
            self::$logger->error("not find providers form redis for $service:$version:$group");
        }
        
        return $ret;     
    }

    //use p2p mode
    private static function getInstanceByP2P($service, $ioTimeOut, $version, $group)
    {
        $ret = NULL;
        if(array_key_exists($service, self::$serviceConsumers))
        {
            $serviceProperty = self::$serviceConsumers[$service];
			if (isset($serviceProperty['url']))
			{
				$ret = Proxy::newProxyInstance($service, self::$appName, $group);

				//设置io超时时间
				$ret->setIOTimeOut($ioTimeOut);

				//设置所用服务的ip地址列表
				$serviceAddr = explode(",",$serviceProperty['url']);
				$serviceUrls = array();
				foreach ($serviceAddr as $index => $addr)
				{
					$tmpUrl = $addr.'/'."{$service}?version={$version}&group={$group}";
					$serviceUrls[] = new FSOFUrl($tmpUrl);
				}
				$ret->setAddress($serviceUrls);
			}
			else
			{
                self::$logger->warn(self::$appName.'.consumer not exist url');
			}
        }
        else 
        {
            self::$logger->warn('service not found on p2p|consumer_app:'.self::$appName.'|provider_service:'.$service);
        }
        
        return $ret;         
    }

    public static function getInstance($consumerInterface, $ioTimeOut = 3, $version = null, $group = null)
    {
    	$ret = NULL;
        $route = '';
        $addressList = 'null';

		//app级组和版本信息
		$group = self::$appGroup;
		$versionList = self::$appVersion;

		if (array_key_exists($consumerInterface, self::$serviceConsumers))
		{
            $serviceProperty = self::$serviceConsumers[$consumerInterface];
			if(isset($serviceProperty['group']))
			{
				$group = $serviceProperty['group'];
			}
			if(isset($serviceProperty['version']))
			{
				$versionList = $serviceProperty['version'];
			}
		}

		try
		{

			//依据配置权重选取版本号
			$version = FSOFCommonUtil::getVersionByWeight($versionList);

			//p2p模式
			if (self::$p2pMode)
			{
				$ret = self::getInstanceByP2P($consumerInterface, $ioTimeOut, $version, $group);
				$route = 'p2p';
			}

			if (empty($ret))
			{
				//registry 模式
				$ret = self::getInstancByRedis($consumerInterface, $ioTimeOut, $version, $group);
				$route = 'auto registry';
			}

			if (empty($ret))
			{
				$errMsg = "current_address:".FSOFSystemUtil::getLocalIP()."|".$consumerInterface;
                throw new ConsumerException($errMsg);
			}
			else
			{
                $addressList = $ret->getAddressStr();
			}
            self::$logger->debug('consumer_app:'.self::$appName.'|app_config_file:'.self::$appConfigFile.
                '|version:'.$version.'|group:'.$group.'|provider_service:'.$consumerInterface.'|route:'.$route.'|addr_list:'.$addressList.'|timeout:'.$ioTimeOut);
        }
        catch (\Exception $e)
        {
            self::$logger->error('consumer_app:'.self::$appName.'|app_config_file:'.self::$appConfigFile.
                '|version:'.$version.'|group:'.$group.'|provider_service:'.$consumerInterface.'|errmsg:'. $e->getMessage().'|exceptionmsg:'.$e);
            throw new ConsumerException($e->getMessage(), $e);
        }
    	return $ret;
    }
}