/**
 * PillarGenerator - Auto-generate support structures for uneven terrain
 * 
 * Creates pillar stacks to fill gaps between foundations and terrain,
 * commonly used in ARK-style building systems.
 * 
 * Usage:
 *   const generator = new PillarGenerator();
 *   const { pillars, cost } = generator.generateForFoundation(foundation, analyzer);
 */

import * as THREE from 'three';

/**
 * Pillar generation strategies
 */
export const PillarStrategy = {
  CORNERS: 'corners',     // Pillars at foundation corners only
  CENTER: 'center',       // Single pillar at center
  GRID: 'grid',           // Grid pattern of pillars
  ADAPTIVE: 'adaptive'    // Based on terrain variation
};

/**
 * Pillar piece definition
 * @typedef {Object} PillarPiece
 * @property {string} id - Unique identifier
 * @property {string} type - Always 'pillar'
 * @property {THREE.Vector3} position - World position
 * @property {number} height - Pillar height
 * @property {number} width - Pillar width
 * @property {Object} material - Material properties
 * @property {boolean} autoGenerated - Whether auto-generated
 * @property {string} parentFoundation - ID of foundation this supports
 */

export class PillarGenerator {
  /**
   * Create pillar generator
   * @param {Object} options - Configuration options
   */
  constructor(options = {}) {
    this.pillarHeight = options.pillarHeight ?? 2.0;
    this.pillarWidth = options.pillarWidth ?? 0.5;
    this.minGap = options.minGap ?? 0.3;
    this.maxPillarsPerStack = options.maxPillarsPerStack ?? 10;
    this.maxTotalPillars = options.maxTotalPillars ?? 50;
    this.strategy = options.strategy ?? PillarStrategy.ADAPTIVE;
    
    // Resource costs per pillar by material
    this.resourceCosts = options.resourceCosts ?? {
      wood: { wood: 20 },
      stone: { stone: 30, wood: 5 },
      metal: { metal: 10, stone: 5 },
      thatch: { thatch: 10, fiber: 5 }
    };
  }

  /**
   * Generate pillars for a foundation
   * @param {Object} foundation - Foundation piece
   * @param {TerrainAnalyzer} analyzer - Terrain analyzer
   * @returns {Object} Generated pillars and metadata
   */
  generateForFoundation(foundation, analyzer) {
    const positions = this.getPillarPositions(foundation);
    const allPillars = [];
    const stackInfo = [];

    for (const pos of positions) {
      const groundY = analyzer.getHeightAt(pos.x, pos.z);
      const gap = foundation.position.y - groundY;
      
      if (gap > this.minGap) {
        const stack = this.generatePillarStack(
          pos,
          groundY,
          foundation.position.y,
          foundation
        );
        
        if (stack.pillars.length > 0) {
          allPillars.push(...stack.pillars);
          stackInfo.push({
            position: pos.clone(),
            groundY,
            gap,
            pillarCount: stack.pillars.length
          });
        }
      }
    }

    // Enforce maximum total pillars
    const limitedPillars = allPillars.slice(0, this.maxTotalPillars);
    const wasLimited = allPillars.length > this.maxTotalPillars;

    return {
      pillars: limitedPillars,
      stacks: stackInfo,
      totalPillars: limitedPillars.length,
      totalHeight: this.calculateTotalHeight(limitedPillars),
      resourceCost: this.calculateResourceCost(limitedPillars, foundation.material),
      wasLimited,
      originalCount: allPillars.length
    };
  }

  /**
   * Get positions where pillars should be placed
   */
  getPillarPositions(foundation) {
    switch (this.strategy) {
      case PillarStrategy.CORNERS:
        return this.getCornerPositions(foundation);
      case PillarStrategy.CENTER:
        return [foundation.position.clone()];
      case PillarStrategy.GRID:
        return this.getGridPositions(foundation);
      case PillarStrategy.ADAPTIVE:
      default:
        return this.getAdaptivePositions(foundation);
    }
  }

  /**
   * Get corner positions of foundation
   */
  getCornerPositions(foundation) {
    const width = foundation.width ?? 4;
    const depth = foundation.depth ?? 4;
    const hw = width / 2;
    const hd = depth / 2;
    const pos = foundation.position;

    const corners = [
      new THREE.Vector3(pos.x - hw, pos.y, pos.z - hd),
      new THREE.Vector3(pos.x + hw, pos.y, pos.z - hd),
      new THREE.Vector3(pos.x + hw, pos.y, pos.z + hd),
      new THREE.Vector3(pos.x - hw, pos.y, pos.z + hd)
    ];

    // Apply rotation if present
    if (foundation.rotation?.y) {
      const euler = new THREE.Euler(0, foundation.rotation.y, 0);
      const center = pos.clone();
      corners.forEach(c => {
        c.sub(center);
        c.applyEuler(euler);
        c.add(center);
      });
    }

    return corners;
  }

  /**
   * Get grid of pillar positions
   */
  getGridPositions(foundation) {
    const width = foundation.width ?? 4;
    const depth = foundation.depth ?? 4;
    const pos = foundation.position;
    const spacing = 2; // Pillar every 2 units
    
    const positions = [];
    const hw = width / 2;
    const hd = depth / 2;

    for (let x = -hw; x <= hw; x += spacing) {
      for (let z = -hd; z <= hd; z += spacing) {
        positions.push(new THREE.Vector3(
          pos.x + x,
          pos.y,
          pos.z + z
        ));
      }
    }

    return positions;
  }

  /**
   * Get adaptive positions based on foundation size
   */
  getAdaptivePositions(foundation) {
    const width = foundation.width ?? 4;
    const depth = foundation.depth ?? 4;
    const area = width * depth;
    
    // Small foundations: corners only
    if (area <= 16) {
      return this.getCornerPositions(foundation);
    }
    
    // Medium foundations: corners + center
    if (area <= 36) {
      const corners = this.getCornerPositions(foundation);
      corners.push(foundation.position.clone());
      return corners;
    }
    
    // Large foundations: grid pattern
    return this.getGridPositions(foundation);
  }

  /**
   * Generate a vertical stack of pillars
   */
  generatePillarStack(position, bottomY, topY, foundation) {
    const gap = topY - bottomY;
    
    if (gap <= this.minGap) {
      return { pillars: [], height: 0 };
    }

    const count = Math.min(
      Math.ceil(gap / this.pillarHeight),
      this.maxPillarsPerStack
    );
    
    const pillars = [];
    let currentY = bottomY;

    for (let i = 0; i < count; i++) {
      const isLast = i === count - 1;
      const remainingGap = topY - currentY;
      const height = isLast 
        ? Math.min(remainingGap, this.pillarHeight)
        : this.pillarHeight;
      
      // Skip very short pillars
      if (height < 0.1) continue;

      const pillar = this.createPillar(
        new THREE.Vector3(position.x, currentY, position.z),
        height,
        foundation,
        i
      );
      
      pillars.push(pillar);
      currentY += height;
    }

    return {
      pillars,
      height: currentY - bottomY
    };
  }

  /**
   * Create a single pillar piece
   */
  createPillar(position, height, foundation, index) {
    const timestamp = Date.now();
    const random = Math.random().toString(36).substr(2, 6);
    
    return {
      id: `pillar_${timestamp}_${index}_${random}`,
      type: 'pillar',
      position: position.clone(),
      height,
      width: this.pillarWidth,
      material: foundation.material ?? { name: 'wood' },
      autoGenerated: true,
      parentFoundation: foundation.id,
      isGrounded: index === 0, // First pillar touches ground
      createdAt: timestamp
    };
  }

  /**
   * Calculate total height of all pillars
   */
  calculateTotalHeight(pillars) {
    return pillars.reduce((sum, p) => sum + p.height, 0);
  }

  /**
   * Calculate resource cost for pillars
   */
  calculateResourceCost(pillars, material) {
    const materialKey = material?.name?.toLowerCase() ?? 'wood';
    const baseCost = this.resourceCosts[materialKey] ?? this.resourceCosts.wood;
    
    const total = {};
    
    for (const resource of Object.keys(baseCost)) {
      total[resource] = 0;
    }

    for (const pillar of pillars) {
      // Scale cost by pillar height
      const heightMultiplier = pillar.height / this.pillarHeight;
      
      for (const [resource, amount] of Object.entries(baseCost)) {
        total[resource] += Math.ceil(amount * heightMultiplier);
      }
    }

    return total;
  }

  /**
   * Validate pillars against resource availability
   */
  validateResources(pillars, foundation, inventory) {
    const cost = this.calculateResourceCost(pillars, foundation.material);
    const missing = {};
    let hasEnough = true;

    for (const [resource, needed] of Object.entries(cost)) {
      const available = inventory[resource] ?? 0;
      if (available < needed) {
        hasEnough = false;
        missing[resource] = needed - available;
      }
    }

    return {
      valid: hasEnough,
      cost,
      missing: hasEnough ? null : missing
    };
  }

  /**
   * Generate pillars with terrain variation analysis
   */
  generateWithAnalysis(foundation, analyzer) {
    const positions = this.getCornerPositions(foundation);
    const analysis = {
      corners: [],
      minGap: Infinity,
      maxGap: 0,
      avgGap: 0
    };

    // Analyze each corner
    for (const pos of positions) {
      const groundY = analyzer.getHeightAt(pos.x, pos.z);
      const gap = foundation.position.y - groundY;
      
      analysis.corners.push({
        position: pos,
        groundY,
        gap,
        needsPillar: gap > this.minGap
      });

      analysis.minGap = Math.min(analysis.minGap, gap);
      analysis.maxGap = Math.max(analysis.maxGap, gap);
    }

    analysis.avgGap = analysis.corners.reduce((sum, c) => sum + c.gap, 0) / 
      analysis.corners.length;
    analysis.variation = analysis.maxGap - analysis.minGap;
    
    // Determine optimal strategy based on analysis
    let strategy = this.strategy;
    if (strategy === PillarStrategy.ADAPTIVE) {
      if (analysis.variation < 0.5) {
        // Uniform terrain: corners only
        strategy = PillarStrategy.CORNERS;
      } else if (analysis.variation < 2) {
        // Moderate variation: corners + maybe center
        strategy = PillarStrategy.CORNERS;
      } else {
        // High variation: full grid for stability
        strategy = PillarStrategy.GRID;
      }
    }

    // Generate with determined strategy
    const originalStrategy = this.strategy;
    this.strategy = strategy;
    const result = this.generateForFoundation(foundation, analyzer);
    this.strategy = originalStrategy;

    return {
      ...result,
      analysis,
      strategyUsed: strategy
    };
  }

  /**
   * Optimize pillar placement to minimize count
   */
  optimizePillars(pillars, foundation) {
    if (pillars.length <= 4) {
      return { pillars, optimized: false };
    }

    // Group pillars by similar height
    const heightGroups = new Map();
    
    for (const pillar of pillars) {
      const roundedHeight = Math.round(pillar.position.y * 2) / 2;
      if (!heightGroups.has(roundedHeight)) {
        heightGroups.set(roundedHeight, []);
      }
      heightGroups.get(roundedHeight).push(pillar);
    }

    // Keep only corner pillars from each height group
    const optimized = [];
    const corners = new Set(['NW', 'NE', 'SE', 'SW']);
    
    for (const [height, group] of heightGroups) {
      if (group.length <= 4) {
        optimized.push(...group);
      } else {
        // Keep 4 most corner-like pillars
        const sorted = group.sort((a, b) => {
          const distA = this.distanceFromCenter(a.position, foundation.position);
          const distB = this.distanceFromCenter(b.position, foundation.position);
          return distB - distA; // Furthest from center first
        });
        optimized.push(...sorted.slice(0, 4));
      }
    }

    return {
      pillars: optimized,
      optimized: optimized.length < pillars.length,
      originalCount: pillars.length,
      optimizedCount: optimized.length
    };
  }

  /**
   * Calculate distance from center
   */
  distanceFromCenter(position, center) {
    const dx = position.x - center.x;
    const dz = position.z - center.z;
    return Math.sqrt(dx * dx + dz * dz);
  }

  /**
   * Create visual preview of pillar placement
   */
  createPreviewMeshes(pillars, options = {}) {
    const color = options.color ?? 0x00ff00;
    const opacity = options.opacity ?? 0.5;
    
    const group = new THREE.Group();
    group.name = 'pillar-preview';

    const material = new THREE.MeshBasicMaterial({
      color,
      transparent: true,
      opacity,
      wireframe: options.wireframe ?? false
    });

    for (const pillar of pillars) {
      const geometry = new THREE.BoxGeometry(
        this.pillarWidth,
        pillar.height,
        this.pillarWidth
      );
      
      const mesh = new THREE.Mesh(geometry, material);
      mesh.position.copy(pillar.position);
      mesh.position.y += pillar.height / 2; // Center pivot
      
      group.add(mesh);
    }

    return group;
  }

  /**
   * Serialize pillars for networking/saving
   */
  serializePillars(pillars) {
    return pillars.map(p => ({
      id: p.id,
      type: p.type,
      position: { x: p.position.x, y: p.position.y, z: p.position.z },
      height: p.height,
      width: p.width,
      material: p.material?.name ?? 'wood',
      autoGenerated: p.autoGenerated,
      parentFoundation: p.parentFoundation
    }));
  }

  /**
   * Deserialize pillars from saved data
   */
  deserializePillars(data) {
    return data.map(p => ({
      ...p,
      position: new THREE.Vector3(p.position.x, p.position.y, p.position.z),
      material: { name: p.material }
    }));
  }

  /**
   * Update configuration
   */
  setOptions(options) {
    if (options.pillarHeight !== undefined) this.pillarHeight = options.pillarHeight;
    if (options.pillarWidth !== undefined) this.pillarWidth = options.pillarWidth;
    if (options.minGap !== undefined) this.minGap = options.minGap;
    if (options.maxPillarsPerStack !== undefined) this.maxPillarsPerStack = options.maxPillarsPerStack;
    if (options.maxTotalPillars !== undefined) this.maxTotalPillars = options.maxTotalPillars;
    if (options.strategy !== undefined) this.strategy = options.strategy;
  }
}

export default PillarGenerator;
