using System;
using System.Collections.Generic;
using System.Linq;
namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands
{
/// The base implementation for a console command.
internal abstract class ConsoleCommand : IConsoleCommand
{
/*********
** Accessors
*********/
/// The command name the user must type.
public string Name { get; }
/// The command description.
public string Description { get; }
/// Whether the command may need to perform logic when the player presses a button. This value shouldn't change.
public bool MayNeedInput { get; }
/// Whether the command may need to perform logic when the game updates. This value shouldn't change.
public bool MayNeedUpdate { get; }
/*********
** Public methods
*********/
/// Handle the command.
/// Writes messages to the console and log file.
/// The command name.
/// The command arguments.
public abstract void Handle(IMonitor monitor, string command, ArgumentParser args);
/// Perform any logic needed on update tick.
/// Writes messages to the console and log file.
public virtual void OnUpdated(IMonitor monitor) { }
/// Perform any logic when input is received.
/// Writes messages to the console and log file.
/// The button that was pressed.
public virtual void OnButtonPressed(IMonitor monitor, SButton button) { }
/*********
** Protected methods
*********/
/// Construct an instance.
/// The command name the user must type.
/// The command description.
/// Whether the command may need to perform logic when the player presses a button.
/// Whether the command may need to perform logic when the game updates.
protected ConsoleCommand(string name, string description, bool mayNeedInput = false, bool mayNeedUpdate = false)
{
this.Name = name;
this.Description = description;
this.MayNeedInput = mayNeedInput;
this.MayNeedUpdate = mayNeedUpdate;
}
/// Log an error indicating incorrect usage.
/// Writes messages to the console and log file.
/// A sentence explaining the problem.
protected void LogUsageError(IMonitor monitor, string error)
{
monitor.Log($"{error} Type 'help {this.Name}' for usage.", LogLevel.Error);
}
/// Log an error indicating a value must be an integer.
/// Writes messages to the console and log file.
protected void LogArgumentNotInt(IMonitor monitor)
{
this.LogUsageError(monitor, "The value must be a whole number.");
}
/// Get an ASCII table to show tabular data in the console.
/// The data type.
/// The data to display.
/// The table header.
/// Returns a set of fields for a data value.
protected string GetTableString(IEnumerable data, string[] header, Func getRow)
{
// get table data
int[] widths = header.Select(p => p.Length).ToArray();
string[][] rows = data
.Select(item =>
{
string[] fields = getRow(item);
if (fields.Length != widths.Length)
throw new InvalidOperationException($"Expected {widths.Length} columns, but found {fields.Length}: {string.Join(", ", fields)}");
for (int i = 0; i < fields.Length; i++)
widths[i] = Math.Max(widths[i], fields[i].Length);
return fields;
})
.ToArray();
// render fields
List lines = new List(rows.Length + 2)
{
header,
header.Select((_, i) => "".PadRight(widths[i], '-')).ToArray()
};
lines.AddRange(rows);
return string.Join(
Environment.NewLine,
lines.Select(line => string.Join(" | ",
line.Select((field, i) => field.PadLeft(widths[i], ' '))
))
);
}
}
}