/**
 * 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.
 */

package io.jafka.producer;

import java.util.List;
import java.util.Properties;

import io.jafka.common.InvalidConfigException;
import io.jafka.common.annotations.ClientSide;
import io.jafka.message.CompressionCodec;
import io.jafka.producer.async.AsyncProducerConfig;
import io.jafka.producer.async.AsyncProducerConfigShared;
import io.jafka.utils.Utils;
import io.jafka.utils.ZKConfig;

/**
 * Configuration for producer
 * @author adyliu (imxylz@gmail.com)
 * @since 1.0
 */
@ClientSide
public class ProducerConfig extends ZKConfig implements SyncProducerConfigShared, AsyncProducerConfigShared {

    private final SyncProducerConfigShared synchConfigShared;

    private final AsyncProducerConfigShared asyncProducerConfigShared;
    /**
     * create config for producer
     * @param props config arguments
     */
    public ProducerConfig(Properties props) {
        super(props);
        synchConfigShared = new SyncProducerConfig(props);
        asyncProducerConfigShared = new AsyncProducerConfig(props);
        check();
    }

    private void check() {
        if (getBrokerList() != null && Utils.getString(props, "partitioner.class", null) != null) {
            throw new InvalidConfigException("partitioner.class cannot be used when broker.list is set");
        }

        // If both broker.list and zk.connect options are specified, throw an exception
        if (getBrokerList() != null && getZkConnect() != null) {
            throw new InvalidConfigException("only one of broker.list and zk.connect can be specified");
        }

        if (getBrokerList() == null && getZkConnect() == null) {
            throw new InvalidConfigException("At least one of zk.connect or broker.list must be specified");
        }
    }

    /**
     * the full properties
     * @return property
     */
    public Properties getProperties() {
        return props;
    }

    public int getBufferSize() {
        return synchConfigShared.getBufferSize();
    }

    public int getConnectTimeoutMs() {
        return synchConfigShared.getConnectTimeoutMs();
    }

    public int getSocketTimeoutMs() {
        return synchConfigShared.getSocketTimeoutMs();
    }


    //public int getMaxMessageSize() {
    //    return synchConfigShared.getMaxMessageSize();
    //}

    public int getQueueTime() {
        return asyncProducerConfigShared.getQueueTime();
    }

    public int getQueueSize() {
        return asyncProducerConfigShared.getQueueSize();
    }

    public int getEnqueueTimeoutMs() {
        return asyncProducerConfigShared.getEnqueueTimeoutMs();
    }

    public int getBatchSize() {
        return asyncProducerConfigShared.getBatchSize();
    }

    public String getSerializerClass() {
        return synchConfigShared.getSerializerClass();
    }

    public String getCbkHandler() {
        return asyncProducerConfigShared.getCbkHandler();
    }

    public Properties getCbkHandlerProperties() {
        return asyncProducerConfigShared.getCbkHandlerProperties();
    }

    public String getEventHandler() {
        return asyncProducerConfigShared.getEventHandler();
    }

    public Properties getEventHandlerProperties() {
        return asyncProducerConfigShared.getEventHandlerProperties();
    }

    /**
     * For bypassing zookeeper based auto partition discovery, use this config to pass in
     * static broker and per-broker partition information. Format-
     * 
     * <pre>
     *      brokerid1:host1:port1[:partitions[:autocreatetopic]], brokerid2:host2:port2[:partitions[:autocreatetopic]]
     * </pre>
     * @return broker list
     */
    public String getBrokerList() {
        return Utils.getString(props, "broker.list", null);
    }

    /** the partitioner class for partitioning events amongst sub-topics
     * @return class of partition rule
     */
    public String getPartitionerClass() {
        return Utils.getString(props, "partitioner.class", DefaultPartitioner.class.getName());
    }

    /**
     * This parameter allows you to specify the compression codec for all data generated by
     * this producer. The default is {@link CompressionCodec#NoCompressionCodec}
     * 
     * @see CompressionCodec#NoCompressionCodec
     * @return default compress code
     */
    public CompressionCodec getCompressionCodec() {
        return CompressionCodec.valueOf(Utils.getInt(props, "compression.codec", 0));
    }

    /**
     * This parameter allows you to set whether compression should be turned * on for
     * particular topics
     * 
     * If the compression codec is anything other than NoCompressionCodec,
     * 
     * Enable compression only for specified topics if any
     * 
     * If the list of compressed topics is empty, then enable the specified compression codec
     * for all topics
     * 
     * If the compression codec is NoCompressionCodec, compression is disabled for all topics
     * @return topic list of compressed
     */
    public List<String> getCompressedTopics() {
        return Utils.getCSVList(Utils.getString(props, "compressed.topics", null));
    }

    /**
     * this parameter specifies whether the messages are sent asynchronously or not. Valid
     * values are
     * 
     * <pre>
     *   async: for asynchronous send 
     *    sync: for synchronous send
     * </pre>
     * @return producer type
     */
    public String getProducerType() {
        return Utils.getString(props, "producer.type", "sync");
    }

    /**
     * The producer using the zookeeper software load balancer maintains a ZK cache that gets
     * updated by the zookeeper watcher listeners. During some events like a broker bounce, the
     * producer ZK cache can get into an inconsistent state, for a small time period. In this
     * time period, it could end up picking a broker partition that is unavailable. When this
     * happens, the ZK cache needs to be updated. This parameter specifies the number of times
     * the producer attempts to refresh this ZK cache.
     * @return times of zookeeper retry
     */
    public int getZkReadRetries() {
        return Utils.getInt(props, "zk.read.num.retries", 3);
    }

    /**
     * If DefaultEventHandler is used, this specifies the number of times to retry if an error
     * is encountered during send. Currently, it is only appropriate when broker.list points to
     * a VIP. If the zk.connect option is used instead, this will not have any effect because
     * with the zk-based producer, brokers are not re-selected upon retry. So retries would go
     * to the same (potentially still down) broker. (KAFKA-253 will help address this.)
     * <br>
     * see https://github.com/apache/kafka/commit/d6b1de35f6b9cd5370c7812790fea8e61618f461
     * @return times of producer retry
     */
    public int getNumRetries() {
        return Utils.getInt(props, "num.retries", 0);
    }
}
