/*
 * Copyright (C) 2017-2017 DataStax Inc.
 *
 * Licensed 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 com.datastax.oss.driver.internal.core;

import com.datastax.oss.driver.api.core.auth.AuthProvider;
import com.datastax.oss.driver.api.core.config.CoreDriverOption;
import com.datastax.oss.driver.api.core.config.DriverConfig;
import com.datastax.oss.driver.api.core.config.DriverConfigProfile;
import com.datastax.oss.driver.internal.core.channel.DefaultWriteCoalescer;
import com.datastax.oss.driver.internal.core.channel.WriteCoalescer;
import com.datastax.oss.driver.internal.core.config.typesafe.TypeSafeDriverConfig;
import com.datastax.oss.driver.internal.core.protocol.ByteBufPrimitiveCodec;
import com.datastax.oss.driver.internal.core.util.Reflection;
import com.datastax.oss.driver.internal.core.util.concurrent.LazyReference;
import com.datastax.oss.protocol.internal.Compressor;
import com.datastax.oss.protocol.internal.FrameCodec;
import com.typesafe.config.ConfigFactory;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;

/**
 * Default implementation of the driver context.
 *
 * <p>All non-constant components are stored as lazy references. Deadlocks or stack overflows may
 * occur if there are cycles in the object graph.
 */
public class DefaultDriverContext implements DriverContext {

  private final LazyReference<DriverConfig> config =
      new LazyReference<>("config", this::buildDriverConfig);
  private final LazyReference<Compressor<ByteBuf>> compressor =
      new LazyReference<>("compressor", this::buildCompressor);
  private final LazyReference<FrameCodec<ByteBuf>> frameCodec =
      new LazyReference<>("frameCodec", this::buildFrameCodec);
  private final LazyReference<ProtocolVersionRegistry> protocolVersionRegistry =
      new LazyReference<>("protocolVersionRegistry", this::buildProtocolVersionRegistry);
  private final LazyReference<NettyOptions> nettyOptions =
      new LazyReference<>("nettyOptions", this::buildNettyOptions);
  private final LazyReference<AuthProvider> authProvider =
      new LazyReference<>("authProvider", this::buildAuthProvider);
  private final LazyReference<WriteCoalescer> writeCoalescer =
      new LazyReference<>("writeCoalescer", this::buildWriteCoalescer);

  private DriverConfig buildDriverConfig() {
    return new TypeSafeDriverConfig(
        ConfigFactory.load().getConfig("datastax-java-driver"), CoreDriverOption.values());
  }

  private Compressor<ByteBuf> buildCompressor() {
    // TODO build alternate implementation if specified in conf
    return Compressor.none();
  }

  protected FrameCodec<ByteBuf> buildFrameCodec() {
    return FrameCodec.defaultClient(
        new ByteBufPrimitiveCodec(nettyOptions().allocator()), compressor());
  }

  protected ProtocolVersionRegistry buildProtocolVersionRegistry() {
    return new ProtocolVersionRegistry();
  }

  protected NettyOptions buildNettyOptions() {
    return new DefaultNettyOptions();
  }

  protected AuthProvider buildAuthProvider() {
    DriverConfigProfile defaultConfig = config().defaultProfile();
    CoreDriverOption classOption = CoreDriverOption.AUTHENTICATION_PROVIDER_CLASS;
    if (defaultConfig.isDefined(classOption)) {
      String className = defaultConfig.getString(classOption);
      return Reflection.buildWithConfig(
          className, AuthProvider.class, defaultConfig, classOption.getPath());
    } else {
      return AuthProvider.NONE;
    }
  }

  private WriteCoalescer buildWriteCoalescer() {
    return new DefaultWriteCoalescer(5);
  }

  @Override
  public DriverConfig config() {
    return config.get();
  }

  @Override
  public Compressor<ByteBuf> compressor() {
    return compressor.get();
  }

  @Override
  public FrameCodec<ByteBuf> frameCodec() {
    return frameCodec.get();
  }

  @Override
  public ProtocolVersionRegistry protocolVersionRegistry() {
    return protocolVersionRegistry.get();
  }

  @Override
  public NettyOptions nettyOptions() {
    return nettyOptions.get();
  }

  @Override
  public AuthProvider authProvider() {
    return authProvider.get();
  }

  @Override
  public WriteCoalescer writeCoalescer() {
    return writeCoalescer.get();
  }
}

@@ -1524,49 +1524,8 @@
 Buf;
-%0Aimport io.netty.buffer.ByteBufAllocator;
 %0A%0A/*
@@ -1842,16 +1842,19 @@
 %3E config
+Ref
  =%0A     
@@ -1971,16 +1971,19 @@
 mpressor
+Ref
  =%0A     
@@ -2102,16 +2102,19 @@
 ameCodec
+Ref
  =%0A     
@@ -2250,16 +2250,19 @@
 Registry
+Ref
  =%0A     
@@ -2402,16 +2402,19 @@
 yOptions
+Ref
  =%0A     
@@ -2532,16 +2532,19 @@
 Provider
+Ref
  =%0A     
@@ -2666,16 +2666,19 @@
 oalescer
+Ref
  =%0A     
@@ -4089,24 +4089,27 @@
 eturn config
+Ref
 .get();%0A  %7D%0A
@@ -4186,16 +4186,19 @@
 mpressor
+Ref
 .get();%0A
@@ -4279,16 +4279,19 @@
 ameCodec
+Ref
 .get();%0A
@@ -4402,16 +4402,19 @@
 Registry
+Ref
 .get();%0A
@@ -4492,16 +4492,19 @@
 yOptions
+Ref
 .get();%0A
@@ -4582,16 +4582,19 @@
 Provider
+Ref
 .get();%0A
@@ -4678,16 +4678,19 @@
 oalescer
+Ref
 .get();%0A

Rename fields in DefaultDriverContext