/*
 * Copyright 2014, Kaazing Corporation. All rights reserved.
 *
 * 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 org.kaazing.nuklei.specification.tcp.cnc;

import static uk.co.real_logic.agrona.IoUtil.mapExistingFile;
import static uk.co.real_logic.agrona.IoUtil.mapNewFile;

import java.io.File;
import java.nio.MappedByteBuffer;

import org.kaazing.k3po.lang.el.Function;
import org.kaazing.k3po.lang.el.spi.FunctionMapperSpi;

import uk.co.real_logic.agrona.concurrent.AtomicBuffer;
import uk.co.real_logic.agrona.concurrent.UnsafeBuffer;
import uk.co.real_logic.agrona.concurrent.broadcast.BroadcastBufferDescriptor;
import uk.co.real_logic.agrona.concurrent.ringbuffer.ManyToOneRingBuffer;
import uk.co.real_logic.agrona.concurrent.ringbuffer.RingBuffer;
import uk.co.real_logic.agrona.concurrent.ringbuffer.RingBufferDescriptor;

public final class Functions
{

    // TODO: compute with alignment
    private static final int META_DATA_LENGTH = 64;

    @Function
    public static Layout mapNew(String filename, int ringCapacity, int broadcastCapacity)
    {
        return new EagerLayout(true, new File(filename), ringCapacity, broadcastCapacity);
    }

    @Function
    public static Layout map(String filename, int ringCapacity, int broadcastCapacity)
    {
        return new DeferredLayout(false, new File(filename), ringCapacity, broadcastCapacity);
    }

    private abstract static class Layout
    {
        private long correlationId;

        public abstract AtomicBuffer getNukleus();

        public abstract AtomicBuffer getController();

        public final long nextCorrelationId()
        {
            RingBuffer ring = new ManyToOneRingBuffer(getNukleus());
            correlationId = ring.nextCorrelationId();
            return correlationId;
        }

        public final long correlationId()
        {
            return correlationId;
        }

    }

    public static final class EagerLayout extends Layout
    {
        private final AtomicBuffer nukleus;
        private final AtomicBuffer controller;

        public EagerLayout(
                boolean overwrite,
                File location,
                int ringCapacity,
                int broadcastCapacity)
        {
            File absolute = location.getAbsoluteFile();
            int metaLength = META_DATA_LENGTH;
            int ringLength = ringCapacity + RingBufferDescriptor.TRAILER_LENGTH;
            int broadcastLength = broadcastCapacity + BroadcastBufferDescriptor.TRAILER_LENGTH;
            MappedByteBuffer buffer = overwrite
                    ? mapNewFile(absolute, metaLength + ringLength + broadcastLength)
                    : mapExistingFile(absolute, location.getAbsolutePath());
            this.nukleus = new UnsafeBuffer(buffer, metaLength, ringLength);
            this.controller = new UnsafeBuffer(buffer, metaLength + ringLength, broadcastLength);
        }

        @Override
        public AtomicBuffer getNukleus()
        {
            return nukleus;
        }

        @Override
        public AtomicBuffer getController()
        {
            return controller;
        }

    }

    public static final class DeferredLayout extends Layout
    {
        private final boolean overwrite;
        private final File location;
        private final int ringCapacity;
        private final int broadcastCapacity;

        private boolean initialized;
        private AtomicBuffer nukleus;
        private AtomicBuffer controller;

        public DeferredLayout(
            boolean overwrite,
            File location,
            int ringCapacity,
            int broadcastCapacity)
        {
            this.overwrite = overwrite;
            this.location = location;
            this.ringCapacity = ringCapacity;
            this.broadcastCapacity = broadcastCapacity;
        }

        @Override
        public AtomicBuffer getNukleus()
        {
            ensureInitialized();
            return nukleus;
        }

        @Override
        public AtomicBuffer getController()
        {
            ensureInitialized();
            return controller;
        }

        void ensureInitialized()
        {
            if (!initialized)
            {
                EagerLayout layout = new EagerLayout(overwrite, location, ringCapacity, broadcastCapacity);
                this.nukleus = layout.getNukleus();
                this.controller = layout.getController();
                this.initialized = true;
            }
        }
    }

    public static class Mapper extends FunctionMapperSpi.Reflective
    {
        public Mapper()
        {
            super(Functions.class);
        }

        @Override
        public String getPrefixName()
        {
            return "cnc";
        }
    }

    private Functions()
    {
        // utility
    }
}

@@ -785,16 +785,68 @@
 NewFile;
+%0Aimport static uk.co.real_logic.agrona.IoUtil.unmap;
 %0A%0Aimport
@@ -1969,32 +1969,57 @@
 tic class Layout
+ implements AutoCloseable
 %0A    %7B%0A        p
@@ -2465,33 +2465,32 @@
 onId;%0A        %7D%0A
-%0A
     %7D%0A%0A    publi
@@ -2535,32 +2535,79 @@
 ds Layout%0A    %7B%0A
+        private final MappedByteBuffer buffer;%0A
         private 
@@ -3151,33 +3151,21 @@
         
-MappedByteBuffer 
+this.
 buffer =
@@ -3742,24 +3742,117 @@
 %0A        %7D%0A%0A
+        @Override%0A        public void close()%0A        %7B%0A            unmap(buffer);%0A        %7D%0A
     %7D%0A%0A    p
@@ -4096,106 +4096,28 @@
 ate 
-boolean initialized;%0A        private AtomicBuffer nukleus;%0A        private AtomicBuffer controller
+EagerLayout delegate
 ;%0A%0A 
@@ -4583,32 +4583,41 @@
          return 
+delegate.
 nukleus;%0A       
@@ -4736,32 +4736,41 @@
          return 
+delegate.
 controller;%0A    
@@ -4788,32 +4788,62 @@
     
-void ensureInitialized()
+@Override%0A        public void close() throws Exception
 %0A   
@@ -4869,20 +4869,24 @@
 if (
-!initialized
+delegate != null
 )%0A  
@@ -4917,97 +4917,23 @@
     
-EagerLayout layout = new EagerLayout(overwrite, location, ringCapacity, broadcastCapacity
+delegate.close(
 );%0A 
@@ -4947,47 +4947,53 @@
     
+%7D%0A
     
-this.nukleus = layout.getNukleus
+    %7D%0A%0A        void ensureInitialized
 ()
-;
 %0A   
@@ -4993,32 +4993,34 @@
 d()%0A        
+%7B%0A
         
 this.control
@@ -5011,89 +5011,144 @@
     
-this.controller = layout.getController();%0A                this.initialized = true
+    if (delegate == null)%0A            %7B%0A                delegate = new EagerLayout(overwrite, location, ringCapacity, broadcastCapacity)
 ;%0A  

Simplify lazy vs eager layout function