# Ruby Calculator - Interactive calculator built with Ruby on Rails
# Author: RSK World (Molla Samser, Founder | Rima Khatun, Designer & Tester)
# Contact: help@rskworld.in | +91 93305 39277
# Website: https://rskworld.in
# Year: 2026
# Description: Build a fully functional calculator using Ruby on Rails with modern UI

class AdvancedCalculatorController < ApplicationController
  def index
    @current_theme = session[:theme] || 'default'
  end
  
  # Graphing Calculator
  def plot_function
    equation = params[:equation]
    x_min = params[:x_min]&.to_f || -10
    x_max = params[:x_max]&.to_f || 10
    points = params[:points]&.to_i || 100
    
    if equation.present?
      @graph_data = GraphCalculator.plot_function(equation, x_min..x_max, points)
      @roots = GraphCalculator.find_roots(equation, x_min..x_max)
      
      respond_to do |format|
        format.html { render partial: 'graph_result' }
        format.json { render json: { graph_data: @graph_data, roots: @roots } }
      end
    else
      respond_to do |format|
        format.html { render partial: 'graph_error', locals: { error: 'Please enter a valid equation' } }
        format.json { render json: { error: 'Please enter a valid equation' }, status: :unprocessable_entity }
      end
    end
  end
  
  def calculate_derivative
    equation = params[:equation]
    x_value = params[:x_value]&.to_f || 0
    
    if equation.present?
      derivative = GraphCalculator.calculate_derivative(equation, x_value)
      
      respond_to do |format|
        format.json { render json: { derivative: derivative, x_value: x_value } }
      end
    else
      respond_to do |format|
        format.json { render json: { error: 'Invalid equation' }, status: :unprocessable_entity }
      end
    end
  end
  
  def calculate_integral
    equation = params[:equation]
    a = params[:a]&.to_f || 0
    b = params[:b]&.to_f || 1
    
    if equation.present?
      integral = GraphCalculator.calculate_integral(equation, a, b)
      
      respond_to do |format|
        format.json { render json: { integral: integral, a: a, b: b } }
      end
    else
      respond_to do |format|
        format.json { render json: { error: 'Invalid equation' }, status: :unprocessable_entity }
      end
    end
  end
  
  # Equation Solver
  def solve_equation
    equation_type = params[:equation_type]
    equation = params[:equation]
    
    result = case equation_type
             when 'linear'
               EquationSolver.solve_linear_equation(equation)
             when 'quadratic'
               EquationSolver.solve_quadratic_equation(equation)
             when 'cubic'
               EquationSolver.solve_cubic_equation(equation)
             when 'system'
               equations = params[:equations].is_a?(Array) ? params[:equations] : [equation]
               EquationSolver.solve_system_of_equations(equations)
             else
               { error: 'Invalid equation type' }
             end
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  # Programming Calculator
  def convert_base
    number = params[:number]
    from_base = params[:from_base].to_i
    to_base = params[:to_base].to_i
    
    result = ProgrammingCalculator.convert_number(number, from_base, to_base)
    
    if result
      respond_to do |format|
        format.json { render json: { result: result, from_base: from_base, to_base: to_base } }
      end
    else
      respond_to do |format|
        format.json { render json: { error: 'Invalid conversion' }, status: :unprocessable_entity }
      end
    end
  end
  
  def bitwise_operation
    num1 = params[:num1]
    num2 = params[:num2]
    operation = params[:operation]
    
    result = ProgrammingCalculator.bitwise_operations(num1, num2, operation)
    
    if result
      respond_to do |format|
        format.json { render json: result }
      end
    else
      respond_to do |format|
        format.json { render json: { error: 'Invalid operation' }, status: :unprocessable_entity }
      end
    end
  end
  
  def get_all_formats
    number = params[:number]
    formats = ProgrammingCalculator.get_all_formats(number)
    
    respond_to do |format|
      format.json { render json: formats }
    end
  end
  
  # Financial Calculator
  def calculate_loan
    principal = params[:principal].to_f
    annual_rate = params[:annual_rate].to_f
    years = params[:years].to_i
    
    result = FinancialCalculator.calculate_loan_payment(principal, annual_rate, years)
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  def generate_amortization
    principal = params[:principal].to_f
    annual_rate = params[:annual_rate].to_f
    years = params[:years].to_i
    
    schedule = FinancialCalculator.generate_amortization_schedule(principal, annual_rate, years)
    
    respond_to do |format|
      format.json { render json: { schedule: schedule } }
    end
  end
  
  def calculate_compound_interest
    principal = params[:principal].to_f
    annual_rate = params[:annual_rate].to_f
    years = params[:years].to_i
    compound_frequency = params[:compound_frequency].to_i
    
    result = FinancialCalculator.calculate_compound_interest(principal, annual_rate, years, compound_frequency)
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  def calculate_tip
    bill_amount = params[:bill_amount].to_f
    tip_percentage = params[:tip_percentage].to_f
    number_of_people = params[:number_of_people].to_i
    
    result = FinancialCalculator.calculate_tip(bill_amount, tip_percentage, number_of_people)
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  # Health Calculator
  def calculate_bmi
    weight = params[:weight].to_f
    height_cm = params[:height_cm].to_f
    
    result = HealthCalculator.calculate_bmi(weight, height_cm)
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  def calculate_bmr
    weight = params[:weight].to_f
    height_cm = params[:height_cm].to_f
    age = params[:age].to_i
    gender = params[:gender]
    
    bmr = HealthCalculator.calculate_bmr(weight, height_cm, age, gender)
    
    respond_to do |format|
      format.json { render json: bmr }
    end
  end
  
  def calculate_body_fat
    weight = params[:weight].to_f
    height_cm = params[:height_cm].to_f
    age = params[:age].to_i
    gender = params[:gender]
    waist_cm = params[:waist_cm]&.to_f
    hip_cm = params[:hip_cm]&.to_f
    neck_cm = params[:neck_cm]&.to_f
    
    result = HealthCalculator.calculate_body_fat_percentage(weight, height_cm, age, gender, waist_cm, hip_cm, neck_cm)
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  def calculate_water_intake
    weight = params[:weight].to_f
    activity_level = params[:activity_level]
    climate = params[:climate] || 'moderate'
    
    result = HealthCalculator.calculate_water_intake(weight, activity_level, climate)
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  # Statistics Calculator
  def calculate_statistics
    numbers = params[:numbers].split(',').map(&:to_f)
    
    if numbers.empty?
      respond_to do |format|
        format.json { render json: { error: 'Please enter valid numbers' }, status: :unprocessable_entity }
      end
      return
    end
    
    stats = {
      count: numbers.length,
      sum: numbers.sum,
      mean: numbers.sum / numbers.length,
      median: calculate_median(numbers),
      mode: calculate_mode(numbers),
      range: numbers.max - numbers.min,
      variance: calculate_variance(numbers),
      standard_deviation: Math.sqrt(calculate_variance(numbers)),
      min: numbers.min,
      max: numbers.max,
      quartiles: calculate_quartiles(numbers)
    }
    
    respond_to do |format|
      format.json { render json: stats }
    end
  end
  
  # Matrix Calculator
  def matrix_operations
    operation = params[:operation]
    matrix_a = parse_matrix(params[:matrix_a])
    matrix_b = params[:matrix_b] ? parse_matrix(params[:matrix_b]) : nil
    
    result = case operation
             when 'add'
               add_matrices(matrix_a, matrix_b)
             when 'multiply'
               multiply_matrices(matrix_a, matrix_b)
             when 'determinant'
               { determinant: calculate_determinant(matrix_a) }
             when 'inverse'
               { inverse: calculate_inverse(matrix_a) }
             when 'transpose'
               { transpose: transpose_matrix(matrix_a) }
             else
               { error: 'Invalid operation' }
             end
    
    respond_to do |format|
      format.json { render json: result }
    end
  end
  
  private
  
  def calculate_median(numbers)
    sorted = numbers.sort
    mid = sorted.length / 2
    
    if sorted.length.odd?
      sorted[mid]
    else
      (sorted[mid - 1] + sorted[mid]) / 2.0
    end
  end
  
  def calculate_mode(numbers)
    frequency = numbers.each_with_object(Hash.new(0)) { |num, hash| hash[num] += 1 }
    max_freq = frequency.values.max
    frequency.select { |_, freq| freq == max_freq }.keys
  end
  
  def calculate_variance(numbers)
    mean = numbers.sum / numbers.length
    sum_of_squares = numbers.sum { |num| (num - mean) ** 2 }
    sum_of_squares / numbers.length
  end
  
  def calculate_quartiles(numbers)
    sorted = numbers.sort
    n = sorted.length
    
    q1_index = (n * 0.25).ceil - 1
    q2_index = (n * 0.5).ceil - 1
    q3_index = (n * 0.75).ceil - 1
    
    {
      q1: sorted[q1_index],
      q2: sorted[q2_index],
      q3: sorted[q3_index]
    }
  end
  
  def parse_matrix(matrix_string)
    return nil unless matrix_string.present?
    
    matrix_string.split(';').map do |row|
      row.split(',').map(&:to_f)
    end
  end
  
  def add_matrices(a, b)
    return { error: 'Matrices must have same dimensions' } unless a.size == b.size && a.first.size == b.first.size
    
    result = a.map.with_index do |row, i|
      row.map.with_index { |val, j| val + b[i][j] }
    end
    
    { result: result }
  end
  
  def multiply_matrices(a, b)
    return { error: 'Invalid matrix dimensions' } unless a.first.size == b.size
    
    result = a.map.with_index do |row, i|
      b.first.size.times.map do |j|
        row.size.times.sum { |k| row[k] * b[k][j] }
      end
    end
    
    { result: result }
  end
  
  def calculate_determinant(matrix)
    return { error: 'Matrix must be square' } unless matrix.size == matrix.first.size
    
    case matrix.size
    when 1
      matrix[0][0]
    when 2
      matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
    else
      # Recursive calculation for larger matrices
      det = 0
      matrix.first.size.times do |j|
        minor = matrix[1..-1].map { |row| row[0...j] + row[j+1..-1] }
        det += ((-1) ** j) * matrix[0][j] * calculate_determinant(minor)
      end
      det
    end
  end
  
  def calculate_inverse(matrix)
    det = calculate_determinant(matrix)
    return { error: 'Matrix is not invertible' } if det == 0
    
    # Simplified 2x2 matrix inverse
    if matrix.size == 2
      inverse = [
        [matrix[1][1] / det, -matrix[0][1] / det],
        [-matrix[1][0] / det, matrix[0][0] / det]
      ]
      { inverse: inverse }
    else
      { error: 'Only 2x2 matrices supported for inverse calculation' }
    end
  end
  
  def transpose_matrix(matrix)
    result = matrix.first.size.times.map do |j|
      matrix.size.times.map { |i| matrix[i][j] }
    end
    { transpose: result }
  end
end
