Source: zpt/ontology/ZPTNamespaces.js

/**
 * ZPTNamespaces.js - Namespace Definitions for ZPT Ontology Integration
 * 
 * This module provides namespace constants and utilities for working with
 * the ZPT (Zoom-Pan-Tilt) ontology and related vocabularies using RDF-Ext.
 * 
 * Provides:
 * - ZPT ontology namespace and term definitions
 * - Related vocabularies (Ragno, PROV-O, SKOS, etc.)
 * - Utility functions for namespace resolution
 * - Mapping between string literals and RDF URIs
 */

import rdf from 'rdf-ext';

/**
 * Create namespace factory function
 * @param {string} baseURI - Base URI for the namespace
 * @returns {Function} Namespace factory function
 */
function createNamespace(baseURI) {
    return function(localName = '') {
        return rdf.namedNode(baseURI + localName);
    };
}

/**
 * Create object proxy for namespace with property access
 * @param {string} baseURI - Base URI for the namespace
 * @returns {Proxy} Namespace proxy object
 */
function createNamespaceObject(baseURI) {
    const ns = createNamespace(baseURI);
    
    return new Proxy(ns, {
        get(target, property) {
            if (typeof property === 'string') {
                return target(property);
            }
            return target[property];
        }
    });
}

// Core Namespaces
export const ZPT_NS = 'http://purl.org/stuff/zpt/';
export const RAGNO_NS = 'http://purl.org/stuff/ragno/';
export const RDF_NS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
export const RDFS_NS = 'http://www.w3.org/2000/01/rdf-schema#';
export const OWL_NS = 'http://www.w3.org/2002/07/owl#';
export const XSD_NS = 'http://www.w3.org/2001/XMLSchema#';
export const SKOS_NS = 'http://www.w3.org/2004/02/skos/core#';
export const PROV_NS = 'http://www.w3.org/ns/prov#';
export const DCTERMS_NS = 'http://purl.org/dc/terms/';

// Namespace Objects
export const ZPT = createNamespaceObject(ZPT_NS);
export const RAGNO = createNamespaceObject(RAGNO_NS);
export const RDF = createNamespaceObject(RDF_NS);
export const RDFS = createNamespaceObject(RDFS_NS);
export const OWL = createNamespaceObject(OWL_NS);
export const XSD = createNamespaceObject(XSD_NS);
export const SKOS = createNamespaceObject(SKOS_NS);
export const PROV = createNamespaceObject(PROV_NS);
export const DCTERMS = createNamespaceObject(DCTERMS_NS);

// ZPT Ontology Terms - Core Classes
export const ZPT_TERMS = {
    // Core Navigation Classes
    NavigationView: ZPT.NavigationView,
    NavigationSession: ZPT.NavigationSession,
    NavigationAgent: ZPT.NavigationAgent,
    NavigationDimension: ZPT.NavigationDimension,
    
    // State Classes
    ZoomState: ZPT.ZoomState,
    PanState: ZPT.PanState,
    TiltState: ZPT.TiltState,
    
    // Dimension Classes
    ZoomLevel: ZPT.ZoomLevel,
    PanDomain: ZPT.PanDomain,
    TiltProjection: ZPT.TiltProjection,
    
    // Ragno Integration Classes
    NavigableCorpuscle: ZPT.NavigableCorpuscle,
    NavigableElement: ZPT.NavigableElement,
    
    // Predefined Zoom Levels
    EntityLevel: ZPT.EntityLevel,
    UnitLevel: ZPT.UnitLevel,
    TextLevel: ZPT.TextLevel,
    CommunityLevel: ZPT.CommunityLevel,
    CorpusLevel: ZPT.CorpusLevel,
    
    // Tilt Projections
    EmbeddingProjection: ZPT.EmbeddingProjection,
    KeywordProjection: ZPT.KeywordProjection,
    GraphProjection: ZPT.GraphProjection,
    TemporalProjection: ZPT.TemporalProjection,
    
    // Pan Domains
    TopicDomain: ZPT.TopicDomain,
    EntityDomain: ZPT.EntityDomain,
    TemporalDomain: ZPT.TemporalDomain,
    GeospatialDomain: ZPT.GeospatialDomain,
    
    // Navigation State Properties
    hasZoomState: ZPT.hasZoomState,
    hasPanState: ZPT.hasPanState,
    hasTiltState: ZPT.hasTiltState,
    
    // State Configuration Properties
    atZoomLevel: ZPT.atZoomLevel,
    withPanDomain: ZPT.withPanDomain,
    withTiltProjection: ZPT.withTiltProjection,
    
    // Selection Properties
    selectedCorpuscle: ZPT.selectedCorpuscle,
    candidateCorpuscle: ZPT.candidateCorpuscle,
    
    // Optimization Properties
    optimizationScore: ZPT.optimizationScore,
    zoomRelevance: ZPT.zoomRelevance,
    panCoverage: ZPT.panCoverage,
    tiltEffectiveness: ZPT.tiltEffectiveness,
    
    // Abstraction Properties
    abstractionOrder: ZPT.abstractionOrder,
    correspondsToRagno: ZPT.correspondsToRagno,
    
    // Process Properties
    navigatedBy: ZPT.navigatedBy,
    partOfSession: ZPT.partOfSession,
    previousView: ZPT.previousView,
    hasPurpose: ZPT.hasPurpose,
    
    // Query Integration Properties
    answersQuery: ZPT.answersQuery,
    queryComplexity: ZPT.queryComplexity,
    
    // Temporal Properties
    navigationTimestamp: ZPT.navigationTimestamp,
    sessionDuration: ZPT.sessionDuration,
    
    // Extended Properties (for temporal constraints, etc.)
    temporalStart: ZPT.temporalStart,
    temporalEnd: ZPT.temporalEnd,
    constrainedByEntity: ZPT.constrainedByEntity,
    hasTemporalConstraint: ZPT.hasTemporalConstraint
};

// Ragno Ontology Terms
export const RAGNO_TERMS = {
    // Core Classes
    Corpuscle: RAGNO.Corpuscle,
    Element: RAGNO.Element,
    Entity: RAGNO.Entity,
    Unit: RAGNO.Unit,
    TextElement: RAGNO.TextElement,
    CommunityElement: RAGNO.CommunityElement,
    Corpus: RAGNO.Corpus,
    
    // Properties
    content: RAGNO.content,
    hasEmbedding: RAGNO.hasEmbedding,
    hasEntity: RAGNO.hasEntity,
    hasUnit: RAGNO.hasUnit,
    hasRelationship: RAGNO.hasRelationship,
    corpuscleType: RAGNO.corpuscleType,
    
    // Relationship types
    Relationship: RAGNO.Relationship,
    relationType: RAGNO.relationType,
    relationSource: RAGNO.relationSource,
    relationTarget: RAGNO.relationTarget
};

// Common XSD Types
export const XSD_TYPES = {
    string: XSD.string,
    integer: XSD.integer,
    float: XSD.float,
    double: XSD.double,
    boolean: XSD.boolean,
    dateTime: XSD.dateTime,
    date: XSD.date,
    time: XSD.time,
    duration: XSD.duration
};

// Common RDF Types
export const RDF_TYPES = {
    type: RDF.type,
    Property: RDF.Property,
    Statement: RDF.Statement,
    subject: RDF.subject,
    predicate: RDF.predicate,
    object: RDF.object
};

// Common RDFS Types
export const RDFS_TYPES = {
    Class: RDFS.Class,
    Resource: RDFS.Resource,
    Literal: RDFS.Literal,
    label: RDFS.label,
    comment: RDFS.comment,
    subClassOf: RDFS.subClassOf,
    subPropertyOf: RDFS.subPropertyOf,
    domain: RDFS.domain,
    range: RDFS.range
};

// SKOS Terms
export const SKOS_TERMS = {
    Concept: SKOS.Concept,
    ConceptScheme: SKOS.ConceptScheme,
    Collection: SKOS.Collection,
    prefLabel: SKOS.prefLabel,
    altLabel: SKOS.altLabel,
    definition: SKOS.definition,
    broader: SKOS.broader,
    narrower: SKOS.narrower,
    related: SKOS.related,
    inScheme: SKOS.inScheme,
    topConceptOf: SKOS.topConceptOf,
    hasTopConcept: SKOS.hasTopConcept
};

// PROV-O Terms
export const PROV_TERMS = {
    Entity: PROV.Entity,
    Activity: PROV.Activity,
    Agent: PROV.Agent,
    wasGeneratedBy: PROV.wasGeneratedBy,
    wasAssociatedWith: PROV.wasAssociatedWith,
    wasDerivedFrom: PROV.wasDerivedFrom,
    startedAtTime: PROV.startedAtTime,
    endedAtTime: PROV.endedAtTime,
    wasInformedBy: PROV.wasInformedBy
};

/**
 * String to URI mapping for ZPT navigation parameters
 */
export const ZPT_STRING_MAPPINGS = {
    // Zoom levels
    zoom: {
        'entity': ZPT_TERMS.EntityLevel,
        'unit': ZPT_TERMS.UnitLevel,
        'text': ZPT_TERMS.TextLevel,
        'micro': ZPT_TERMS.TextLevel, // Alias for text level
        'community': ZPT_TERMS.CommunityLevel,
        'corpus': ZPT_TERMS.CorpusLevel
    },
    
    // Tilt projections
    tilt: {
        'keywords': ZPT_TERMS.KeywordProjection,
        'embedding': ZPT_TERMS.EmbeddingProjection,
        'graph': ZPT_TERMS.GraphProjection,
        'temporal': ZPT_TERMS.TemporalProjection
    },
    
    // Pan domains
    pan: {
        'topic': ZPT_TERMS.TopicDomain,
        'entity': ZPT_TERMS.EntityDomain,
        'temporal': ZPT_TERMS.TemporalDomain,
        'geospatial': ZPT_TERMS.GeospatialDomain
    }
};

/**
 * URI to string mapping for reverse lookups
 */
export const ZPT_URI_MAPPINGS = {
    // Zoom levels
    [ZPT_TERMS.EntityLevel.value]: 'entity',
    [ZPT_TERMS.UnitLevel.value]: 'unit',
    [ZPT_TERMS.TextLevel.value]: 'text',
    [ZPT_TERMS.CommunityLevel.value]: 'community',
    [ZPT_TERMS.CorpusLevel.value]: 'corpus',
    
    // Tilt projections
    [ZPT_TERMS.KeywordProjection.value]: 'keywords',
    [ZPT_TERMS.EmbeddingProjection.value]: 'embedding',
    [ZPT_TERMS.GraphProjection.value]: 'graph',
    [ZPT_TERMS.TemporalProjection.value]: 'temporal',
    
    // Pan domains
    [ZPT_TERMS.TopicDomain.value]: 'topic',
    [ZPT_TERMS.EntityDomain.value]: 'entity',
    [ZPT_TERMS.TemporalDomain.value]: 'temporal',
    [ZPT_TERMS.GeospatialDomain.value]: 'geospatial'
};

/**
 * Utility Functions for Namespace Management
 */
export const NamespaceUtils = {
    /**
     * Resolve a string parameter to ZPT URI
     * @param {string} type - Parameter type (zoom, tilt, pan)
     * @param {string} value - String value to resolve
     * @returns {NamedNode|null} ZPT URI or null if not found
     */
    resolveStringToURI(type, value) {
        return ZPT_STRING_MAPPINGS[type]?.[value] || null;
    },
    
    /**
     * Resolve a ZPT URI to string parameter
     * @param {NamedNode|string} uri - ZPT URI to resolve
     * @returns {string|null} String value or null if not found
     */
    resolveURIToString(uri) {
        const uriValue = typeof uri === 'string' ? uri : uri.value;
        return ZPT_URI_MAPPINGS[uriValue] || null;
    },
    
    /**
     * Check if a URI belongs to ZPT namespace
     * @param {NamedNode|string} uri - URI to check
     * @returns {boolean} True if URI is in ZPT namespace
     */
    isZPTURI(uri) {
        const uriValue = typeof uri === 'string' ? uri : uri.value;
        return uriValue.startsWith(ZPT_NS);
    },
    
    /**
     * Check if a URI belongs to Ragno namespace
     * @param {NamedNode|string} uri - URI to check
     * @returns {boolean} True if URI is in Ragno namespace
     */
    isRagnoURI(uri) {
        const uriValue = typeof uri === 'string' ? uri : uri.value;
        return uriValue.startsWith(RAGNO_NS);
    },
    
    /**
     * Extract local name from URI
     * @param {NamedNode|string} uri - URI to extract from
     * @returns {string} Local name part of URI
     */
    getLocalName(uri) {
        const uriValue = typeof uri === 'string' ? uri : uri.value;
        return uriValue.split('/').pop().split('#').pop();
    },
    
    /**
     * Create a prefixed name for debugging/display
     * @param {NamedNode|string} uri - URI to create prefixed name for
     * @returns {string} Prefixed name (e.g., "zpt:EntityLevel")
     */
    toPrefixedName(uri) {
        const uriValue = typeof uri === 'string' ? uri : uri.value;
        
        if (uriValue.startsWith(ZPT_NS)) {
            return 'zpt:' + uriValue.substring(ZPT_NS.length);
        } else if (uriValue.startsWith(RAGNO_NS)) {
            return 'ragno:' + uriValue.substring(RAGNO_NS.length);
        } else if (uriValue.startsWith(RDF_NS)) {
            return 'rdf:' + uriValue.substring(RDF_NS.length);
        } else if (uriValue.startsWith(RDFS_NS)) {
            return 'rdfs:' + uriValue.substring(RDFS_NS.length);
        } else if (uriValue.startsWith(SKOS_NS)) {
            return 'skos:' + uriValue.substring(SKOS_NS.length);
        } else if (uriValue.startsWith(PROV_NS)) {
            return 'prov:' + uriValue.substring(PROV_NS.length);
        } else if (uriValue.startsWith(XSD_NS)) {
            return 'xsd:' + uriValue.substring(XSD_NS.length);
        }
        
        return uriValue; // Return full URI if no known prefix
    },
    
    /**
     * Get all defined zoom levels
     * @returns {Array} Array of zoom level URIs
     */
    getAllZoomLevels() {
        return [
            ZPT_TERMS.EntityLevel,
            ZPT_TERMS.UnitLevel,
            ZPT_TERMS.TextLevel,
            ZPT_TERMS.CommunityLevel,
            ZPT_TERMS.CorpusLevel
        ];
    },
    
    /**
     * Get all defined tilt projections
     * @returns {Array} Array of tilt projection URIs
     */
    getAllTiltProjections() {
        return [
            ZPT_TERMS.KeywordProjection,
            ZPT_TERMS.EmbeddingProjection,
            ZPT_TERMS.GraphProjection,
            ZPT_TERMS.TemporalProjection
        ];
    },
    
    /**
     * Get all defined pan domains
     * @returns {Array} Array of pan domain URIs
     */
    getAllPanDomains() {
        return [
            ZPT_TERMS.TopicDomain,
            ZPT_TERMS.EntityDomain,
            ZPT_TERMS.TemporalDomain,
            ZPT_TERMS.GeospatialDomain
        ];
    },
    
    /**
     * Validate navigation parameters against ZPT ontology
     * @param {Object} params - Navigation parameters
     * @returns {Object} Validation result with errors and warnings
     */
    validateNavigationParams(params) {
        const errors = [];
        const warnings = [];
        
        // Validate zoom level
        if (params.zoom) {
            const zoomURI = typeof params.zoom === 'string' ? 
                this.resolveStringToURI('zoom', params.zoom) : params.zoom;
            if (!zoomURI || !this.getAllZoomLevels().some(level => level.equals(zoomURI))) {
                errors.push(`Invalid zoom level: ${params.zoom}`);
            }
        }
        
        // Validate tilt projection
        if (params.tilt) {
            const tiltURI = typeof params.tilt === 'string' ? 
                this.resolveStringToURI('tilt', params.tilt) : params.tilt;
            if (!tiltURI || !this.getAllTiltProjections().some(proj => proj.equals(tiltURI))) {
                errors.push(`Invalid tilt projection: ${params.tilt}`);
            }
        }
        
        // Validate pan domains
        if (params.pan && params.pan.domains) {
            const domains = Array.isArray(params.pan.domains) ? params.pan.domains : [params.pan.domains];
            domains.forEach(domain => {
                const domainURI = typeof domain === 'string' ? 
                    this.resolveStringToURI('pan', domain) : domain;
                if (!domainURI || !this.getAllPanDomains().some(d => d.equals(domainURI))) {
                    warnings.push(`Unknown pan domain: ${domain}`);
                }
            });
        }
        
        return {
            valid: errors.length === 0,
            errors,
            warnings
        };
    },
    
    /**
     * Get namespace mappings for API responses
     * @returns {Object} Namespace prefix to URI mappings
     */
    getNamespaces() {
        return SPARQL_PREFIXES;
    },
    
    /**
     * Get zoom levels with descriptions for API responses
     * @returns {Array} Array of zoom level objects with string, uri, and description
     */
    getZoomLevels() {
        return [
            {
                string: 'entity',
                uri: ZPT_TERMS.EntityLevel,
                description: 'Individual concepts and named entities'
            },
            {
                string: 'unit',
                uri: ZPT_TERMS.UnitLevel,
                description: 'Semantic units and text passages'
            },
            {
                string: 'text',
                uri: ZPT_TERMS.TextLevel,
                description: 'Raw text elements and fragments'
            },
            {
                string: 'community',
                uri: ZPT_TERMS.CommunityLevel,
                description: 'Topic clusters and concept groups'
            },
            {
                string: 'corpus',
                uri: ZPT_TERMS.CorpusLevel,
                description: 'Entire corpus view'
            },
            {
                string: 'micro',
                uri: ZPT_TERMS.TextLevel, // Alias for text level
                description: 'Sub-entity components'
            }
        ];
    },
    
    /**
     * Get tilt projections with descriptions for API responses
     * @returns {Array} Array of tilt projection objects with string, uri, and description
     */
    getTiltProjections() {
        return [
            {
                string: 'keywords',
                uri: ZPT_TERMS.KeywordProjection,
                description: 'Keyword-based analysis and matching'
            },
            {
                string: 'embedding',
                uri: ZPT_TERMS.EmbeddingProjection,
                description: 'Vector similarity using embeddings'
            },
            {
                string: 'graph',
                uri: ZPT_TERMS.GraphProjection,
                description: 'Graph structure and connectivity analysis'
            },
            {
                string: 'temporal',
                uri: ZPT_TERMS.TemporalProjection,
                description: 'Time-based organization and sequencing'
            }
        ];
    },
    
    /**
     * Get pan domains with descriptions for API responses
     * @returns {Array} Array of pan domain objects with string, uri, and description
     */
    getPanDomains() {
        return [
            {
                string: 'topic',
                uri: ZPT_TERMS.TopicDomain,
                description: 'Subject/topic constraints'
            },
            {
                string: 'entity',
                uri: ZPT_TERMS.EntityDomain,
                description: 'Entity-based filtering'
            },
            {
                string: 'temporal',
                uri: ZPT_TERMS.TemporalDomain,
                description: 'Time period constraints'
            },
            {
                string: 'geographic',
                uri: ZPT_TERMS.GeospatialDomain,
                description: 'Location-based filtering'
            }
        ];
    }
};

/**
 * Prefix map for SPARQL queries
 */
export const SPARQL_PREFIXES = {
    zpt: ZPT_NS,
    ragno: RAGNO_NS,
    rdf: RDF_NS,
    rdfs: RDFS_NS,
    owl: OWL_NS,
    xsd: XSD_NS,
    skos: SKOS_NS,
    prov: PROV_NS,
    dcterms: DCTERMS_NS
};

/**
 * Generate SPARQL prefix string
 * @param {Array<string>} prefixes - Array of prefix names to include
 * @returns {string} SPARQL prefix declarations
 */
export function getSPARQLPrefixes(prefixes = Object.keys(SPARQL_PREFIXES)) {
    return prefixes
        .map(prefix => `PREFIX ${prefix}: <${SPARQL_PREFIXES[prefix]}>`)
        .join('\n') + '\n';
}

export default {
    ZPT,
    RAGNO,
    RDF,
    RDFS,
    OWL,
    XSD,
    SKOS,
    PROV,
    DCTERMS,
    ZPT_TERMS,
    RAGNO_TERMS,
    ZPT_STRING_MAPPINGS,
    ZPT_URI_MAPPINGS,
    NamespaceUtils,
    SPARQL_PREFIXES,
    getSPARQLPrefixes
};