/*
 * Copyright 2012 Thomas Bocek
 * 
 * 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 net.tomp2p.p2p.builder;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import net.tomp2p.futures.FuturePut;
import net.tomp2p.p2p.Peer;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.storage.Data;

public class PutBuilder extends DHTBuilder<PutBuilder> {
    private Entry<Number640, Data> data;

    private Map<Number640, Data> dataMap;

    private Map<Number160, Data> dataMapConvert;

    //
    private boolean putIfAbsent = false;

    public PutBuilder(Peer peer, Number160 locationKey) {
        super(peer, locationKey);
        self(this);
    }

    public Entry<Number640, Data> getData() {
        return data;
    }

    public PutBuilder setData(final Data data) {
        return setData(locationKey, domainKey == null ? Number160.ZERO : domainKey, Number160.ZERO,
                Number160.ZERO, data);
    }

    public PutBuilder setData(final Number160 contentKey, final Data data) {
        return setData(locationKey, domainKey == null ? Number160.ZERO : domainKey, contentKey,
                Number160.ZERO, data);
    }

    public PutBuilder setData(final Number160 domainKey, final Number160 contentKey, final Data data) {
        return setData(locationKey, domainKey, contentKey, Number160.ZERO, data);
    }

    public PutBuilder setData(final Data data, final Number160 versionKey) {
        return setData(locationKey, domainKey == null ? Number160.ZERO : domainKey, Number160.ZERO,
                versionKey, data);
    }

    public PutBuilder setData(final Number160 contentKey, final Data data, final Number160 versionKey) {
        return setData(locationKey, domainKey == null ? Number160.ZERO : domainKey, contentKey, versionKey,
                data);
    }

    public PutBuilder setData(final Number160 locationKey, final Number160 domainKey,
            final Number160 contentKey, final Number160 versionKey, final Data data) {
        this.data = new Entry<Number640, Data>() {
            @Override
            public Data setValue(Data value) {
                return null;
            }

            @Override
            public Data getValue() {
                return data;
            }

            @Override
            public Number640 getKey() {
                return new Number640(locationKey, domainKey, contentKey, versionKey);
            }
        };
        return this;
    }

    @Override
    public PutBuilder setDomainKey(final Number160 domainKey) {
        // if we set data before we set domain key, we need to adapt the domain key of the data object
        if (data != null) {
            setData(data.getKey().getLocationKey(), domainKey, data.getKey().getContentKey(), data.getKey()
                    .getVersionKey(), data.getValue());
        }
        super.setDomainKey(domainKey);
        return this;
    }

    public PutBuilder setObject(Object object) throws IOException {
        return setData(new Data(object));
    }

    public PutBuilder setKeyObject(Number160 contentKey, Object object) throws IOException {
        return setData(contentKey, new Data(object));
    }

    public Map<Number640, Data> getDataMap() {
        return dataMap;
    }

    public PutBuilder setDataMap(Map<Number640, Data> dataMap) {
        this.dataMap = dataMap;
        return this;
    }

    public Map<Number160, Data> getDataMapContent() {
        return dataMapConvert;
    }

    public PutBuilder setDataMapContent(Map<Number160, Data> dataMapConvert) {
        this.dataMapConvert = dataMapConvert;
        return this;
    }

    public boolean isPutIfAbsent() {
        return putIfAbsent;
    }

    public PutBuilder setPutIfAbsent(boolean putIfAbsent) {
        this.putIfAbsent = putIfAbsent;
        return this;
    }

    public PutBuilder setPutIfAbsent() {
        this.putIfAbsent = true;
        return this;
    }

    public FuturePut start() {
        preBuild("put-builder");
        if (data != null) {
            if (dataMap == null) {
                setDataMap(new HashMap<Number640, Data>(1));
            }
            getDataMap().put(getData().getKey(), getData().getValue());
        }
        if (dataMap == null && dataMapConvert == null) {
            throw new IllegalArgumentException(
                    "You must either set data via setDataMap() or setData(). Cannot add nothing.");
        }

        return peer.getDistributedHashMap().put(this);
    }
}

@@ -5000,32 +5000,329 @@
 ng.%22);%0A        %7D
+%0A        if (locationKey == null) %7B%0A            throw new IllegalArgumentException(%22You must provide a location key.%22);%0A        %7D%0A        if (domainKey == null) %7B%0A            domainKey = Number160.ZERO;%0A        %7D%0A        if (versionKey == null) %7B%0A            versionKey = Number160.ZERO;%0A        %7D
 %0A%0A        return

version key could be null