package com.zenika.cudf.parser;

import com.zenika.cudf.model.*;
import com.zenika.cudf.parser.model.CUDFParsedDescriptor;
import com.zenika.cudf.parser.model.ParsedBinary;
import com.zenika.cudf.parser.model.ParsedPreamble;
import com.zenika.cudf.parser.model.ParsedRequest;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author Adrien Lecharpentier <adrien.lecharpentier@zenika.com>
 */
public abstract class AbstractDeserializer {

    public CUDFDescriptor deserialize() throws ParsingException {
        CUDFParsedDescriptor parsedDescriptor = parseCudf();
        CUDFDescriptor cudfDescriptor = new CUDFDescriptor();

        Preamble preamble = processPreamble(parsedDescriptor);
        Binaries binaries = processParsedBinaries(parsedDescriptor);
        Request request = processParsedRequest(parsedDescriptor, binaries);

        cudfDescriptor.setPreamble(preamble);
        cudfDescriptor.setBinaries(binaries);
        cudfDescriptor.setRequest(request);

        return cudfDescriptor;
    }

    protected abstract CUDFParsedDescriptor parseCudf() throws ParsingException;

    private Preamble processPreamble(CUDFParsedDescriptor parsedDescriptor) {
        ParsedPreamble parsedPreamble = parsedDescriptor.getPreamble();
        Preamble preamble = new Preamble();
        preamble.setProperties(parsedPreamble.getProperties());
        preamble.setUnivChecksum(parsedPreamble.getUnivChecksum());
        preamble.setStatusChecksum(parsedPreamble.getStatusChecksum());
        preamble.setReqChecksum(parsedPreamble.getReqChecksum());
        return preamble;
    }

    private Binaries processParsedBinaries(CUDFParsedDescriptor parsedDescriptor) {
        Set<ParsedBinary> parsedBinaries = parsedDescriptor.getPackages();
        Map<BinaryId, ParsedBinary> parsedRevisions = buildParsedRevisions(parsedBinaries);

        Binaries binaries = new DefaultBinaries();
        for (ParsedBinary parsedBinary : parsedBinaries) {
            Binary binary = new Binary(parsedBinary.getBinaryId());
            binary.setRevision(parsedBinary.getRevision());
            binary.setInstalled(parsedBinary.isInstalled());
            binary.setType(parsedBinary.getType());
            Set<Binary> dependencies = processParsedBinaryDependencies(parsedBinary, parsedRevisions);
            binary.setDependencies(dependencies);
            binaries.addBinary(binary);
        }
        return binaries;
    }

    private Map<BinaryId, ParsedBinary> buildParsedRevisions(Set<ParsedBinary> parsedBinaries) {
        HashMap<BinaryId, ParsedBinary> revisions = new HashMap<BinaryId, ParsedBinary>();
        for (ParsedBinary parsedBinary : parsedBinaries) {
            revisions.put(parsedBinary.getBinaryId(), parsedBinary);
        }
        return revisions;
    }

    private Set<Binary> processParsedBinaryDependencies(ParsedBinary parsedBinary, Map<BinaryId, ParsedBinary> parsedRevisions) {
        Set<Binary> dependencies = new HashSet<Binary>();
        for (BinaryId binaryId : parsedBinary.getDependencies()) {
            ParsedBinary parsedDependency = parsedRevisions.get(binaryId);
            Binary dependency = new Binary(parsedDependency.getBinaryId());
            dependency.setRevision(parsedDependency.getRevision());
            dependency.setInstalled(parsedDependency.isInstalled());
            dependency.setType(parsedDependency.getType());
            dependencies.add(dependency);
        }
        return dependencies;
    }

    private Request processParsedRequest(CUDFParsedDescriptor parsedDescriptor, Binaries binaries) {
        Request request = new Request();
        ParsedRequest parsedRequest = parsedDescriptor.getRequest();
        Map<BinaryId, Binary> revisions = buildRevisionMap(binaries.getAllBinaries());
        request.setInstall(processBinaryIdSet(revisions, parsedRequest.getInstall()));
        request.setUpdate(processBinaryIdSet(revisions, parsedRequest.getUpdate()));
        request.setRemove(processBinaryIdSet(revisions, parsedRequest.getRemove()));
        return request;
    }

    private HashMap<BinaryId, Binary> buildRevisionMap(Set<Binary> binaries) {
        HashMap<BinaryId, Binary> revisions = new HashMap<BinaryId, Binary>();
        for (Binary binary : binaries) {
            revisions.put(binary.getBinaryId(), binary);
        }
        return revisions;
    }

    private Set<Binary> processBinaryIdSet(Map<BinaryId, Binary> revisions, Set<BinaryId> parsedInstall) {
        Set<Binary> binaries = new HashSet<Binary>();
        for (BinaryId binaryId : parsedInstall) {
            Binary binary = revisions.get(binaryId);
            binaries.add(binary);
        }
        return binaries;
    }
}

@@ -1712,32 +1712,56 @@
 arsedDescriptor)
+ throws ParsingException
  %7B%0A        Set%3CP
@@ -2987,19 +2987,43 @@
 visions)
+ throws ParsingException
  %7B%0A
-
         
@@ -3135,24 +3135,66 @@
 encies()) %7B%0A
+            validateDependency(binaryId);%0A
             
@@ -3573,32 +3573,32 @@
 ncy);%0A        %7D%0A
-
         return d
@@ -3609,32 +3609,354 @@
 dencies;%0A    %7D%0A%0A
+    private void validateDependency(BinaryId binaryId) throws ParsingException %7B%0A        if (binaryId.getVersion() %3C= 0) %7B%0A            throw new ParsingException(%22The dependency binary id version of %22 + binaryId.getOrganisation() + ParsedBinary.SEPARATOR + binaryId.getName() + %22 must be either than 0%22);%0A        %7D%0A    %7D%0A%0A
     private Requ

Fix NPE in the abstract descriptor when a binary dependency. Refer to #6, close #7.