package org.rcsb.cif.schema;

import org.rcsb.cif.model.Block;
import org.rcsb.cif.model.CifFile;

import java.util.List;
import java.util.stream.Collectors;

/**
 * The schema is implemented by wrapping a {@link CifFile} and all block, category, and column data therein in dedicated
 * classes that provide the schema (category names, column names, and column types) defined by some dictionary.
 *
 * <p>The implementation achieves this behavior by reimplementing all model interfaces ({@link CifFile}, {@link Block},
 * {@link org.rcsb.cif.model.Category}, {@link org.rcsb.cif.model.Column}) using a delegating approach. Each
 * schema-supporting wraps a schema-unaware instance of the original data. Schemata subclass all model instances and
 * are than able to enumerate all child nodes in the data model and provide appropriate types by additional
 * schema-specific code. Code is generated by {@link org.rcsb.cif.schema.generator.SchemaGenerator} from dictionary files.
 * @param <B> the type of Block instances according to this schema
 */
public abstract class DelegatingCifFile<B extends DelegatingBlock> implements CifFile {
    protected final CifFile delegate;
    private final List<B> blocks;

    protected DelegatingCifFile(CifFile delegate) {
        this.delegate = delegate;
        this.blocks = delegate.getBlocks()
                .stream()
                .map(this::getTypedBlock)
                .collect(Collectors.toList());
    }

    @Override
    public List<B> getBlocks() {
        return blocks;
    }

    /**
     * Convenience function to give access to all data in this file. {@link CifFile} does not provide this functionality
     * to give users a subtle indication whether a schema is in place or the model is accessed in the generic,
     * 'low-level' mode.
     * @return the typed block that wraps all data of this file
     */
    public B getFirstBlock() {
        return blocks.get(0);
    }

    protected abstract B getTypedBlock(Block block);
}
