public async Task <IActionResult> DeleteMoldAsync(int id, CancellationToken ct)
        {
            if (id <= 0)
            {
                return(NotFound());
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var mold = await db.Molds
                           .Where(m => m.Controller.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                           .SingleOrDefaultAsync(m => m.ID == id, ct)
                           .ConfigureAwait(false);

                if (mold == null)
                {
                    return(NotFound());
                }

                await db.Entry(mold).Collection(m => m.MoldSettings).LoadAsync(ct).ConfigureAwait(false);

                var mx = new MoldX(mold);

                db.MoldSettings.RemoveRange(mold.MoldSettings);
                db.Molds.Remove(mold);
                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(mx));
            }
        }
        public async Task <IActionResult> DeleteControllerAsync(int id, CancellationToken ct)
        {
            if (id <= 0)
            {
                return(NotFound());
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var controller = await db.Controllers
                                 .Where(c => c.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                                 .SingleOrDefaultAsync(c => c.ID == id, ct)
                                 .ConfigureAwait(false);

                if (controller == null)
                {
                    return(NotFound());
                }

                db.Controllers.Remove(controller);
                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(new ControllerX(controller)));
            }
        }
        public async Task <IActionResult> UpdateMoldSettingAsync(int moldId, int offset, [FromBody] MoldSettingX delta, CancellationToken ct)
        {
            if (moldId <= 0)
            {
                return(NotFound());
            }
            if (offset < 0)
            {
                return(NotFound());
            }
            if (delta.MoldId != 0)
            {
                return(BadRequest("Mold ID cannot be changed."));
            }
            if (delta.Offset != 0)
            {
                return(BadRequest("Offset cannot be changed."));
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var mold = await db.Molds.AsNoTracking().Include(m => m.MoldSettings)
                           .Where(m => m.Controller.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                           .SingleOrDefaultAsync(m => m.ID == moldId, ct)
                           .ConfigureAwait(false);

                if (mold == null)
                {
                    return(NotFound());
                }

                var ms = await db.MoldSettings.SingleOrDefaultAsync(s => s.MoldId == moldId && s.Offset == offset, ct).ConfigureAwait(false);

                if (ms == null)
                {
                    return(NotFound());
                }

                if (delta.m_RawData.HasValue)
                {
                    ms.RawData = delta.RawData.Value;
                }

                if (delta.Variable.HasValue)
                {
                    ms.Variable = (delta.Variable.Value == 0) ? (int?)null : delta.Variable.Value;
                }

                delta.Modified = DateTime.Now;

                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(new MoldSettingX(ms)));
            }
        }
        public async Task <IActionResult> AddMoldAsync([FromBody] MoldX mold, CancellationToken ct)
        {
            if (mold.ID != 0)
            {
                return(BadRequest("ID must be set to zero."));
            }

            if (string.IsNullOrWhiteSpace(mold.Name))
            {
                return(BadRequest($"Invalid mold name: [{mold.Name}]."));
            }
            if (mold.ControllerId.HasValue && mold.ControllerId <= 0)
            {
                return(BadRequest($"Invalid controller: {mold.ControllerId}."));
            }

            mold.Name     = mold.Name.Trim();
            mold.Modified = null;

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                if (mold.ControllerId.HasValue)
                {
                    var ctrl = await db.Controllers
                               .Where(c => c.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                               .SingleOrDefaultAsync(c => c.ID == mold.ControllerId, ct)
                               .ConfigureAwait(false);

                    if (ctrl == null)
                    {
                        return(NotFound());
                    }
                }
                var cx = await db.Molds.AsNoTracking()
                         .Where(m => m.ControllerId == mold.ControllerId)
                         .SingleOrDefaultAsync(m => m.Name.Equals(mold.Name, StringComparison.OrdinalIgnoreCase), ct)
                         .ConfigureAwait(false);

                if (cx != null)
                {
                    return(BadRequest($"Controller/Mold [{mold.ControllerId}/{mold.Name}] already exists."));
                }

                var mx = mold.GetBase();

                db.Molds.Add(mx);
                db.MoldSettings.AddRange(mx.MoldSettings);

                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Created($"molds/{mx.ID}", new MoldX(mx)));
            }
        }
        public async Task <IActionResult> AddMoldSettingAsync(int moldId, int offset, ushort value, int variable, CancellationToken ct)
        {
            if (moldId <= 0)
            {
                return(NotFound());
            }
            if (offset < 0)
            {
                return(NotFound());
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var mold = await db.Molds.AsNoTracking().Include(m => m.MoldSettings)
                           .Where(m => m.Controller.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                           .SingleOrDefaultAsync(m => m.ID == moldId, ct)
                           .ConfigureAwait(false);

                if (mold == null)
                {
                    return(NotFound());
                }

                var mx = await db.MoldSettings.SingleOrDefaultAsync(s => s.MoldId == moldId && s.Offset == offset, ct).ConfigureAwait(false);

                if (mx != null)
                {
                    return(BadRequest($"Mold/Offset [{moldId}/{offset}] already exists."));
                }

                var ms = new MoldSetting()
                {
                    MoldId   = moldId,
                    Offset   = (short)offset,
                    Created  = DateTime.Now,
                    RawData  = value,
                    Variable = variable
                };

                db.MoldSettings.Add(ms);
                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Created($"molds/{ms.MoldId}/settings", new MoldSettingX(ms)));
            }
        }
        public async Task <IActionResult> UpdateTerminalConfig(string orgId, [FromBody] JToken json, CancellationToken ct)
        {
            using (var db = new ConfigDB()) {
                var config = await db.TerminalConfigs.SingleOrDefaultAsync(c => c.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase), ct).ConfigureAwait(false);

                if (config == null)
                {
                    return(NotFound());
                }

                config.Text     = json.ToString();
                config.Modified = DateTime.Now;

                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(NoContent());
            }
        }
        public async Task <IActionResult> DeleteMoldSettingAsync(int moldId, int offset, CancellationToken ct)
        {
            if (moldId <= 0)
            {
                return(NotFound());
            }
            if (offset < 0)
            {
                return(NotFound());
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var mold = await db.Molds.AsNoTracking().Include(m => m.MoldSettings)
                           .Where(m => m.Controller.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                           .SingleOrDefaultAsync(m => m.ID == moldId, ct)
                           .ConfigureAwait(false);

                if (mold == null)
                {
                    return(NotFound());
                }

                var ms = await db.MoldSettings.SingleOrDefaultAsync(s => s.MoldId == moldId && s.Offset == offset, ct).ConfigureAwait(false);

                if (ms == null)
                {
                    return(NotFound());
                }

                db.MoldSettings.Remove(ms);
                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(new MoldSettingX(ms)));
            }
        }
        public async Task <IActionResult> UpdateControllerAsync(int id, [FromBody] ControllerX delta, CancellationToken ct)
        {
            if (id <= 0)
            {
                return(NotFound());
            }
            if (delta.ID != 0)
            {
                return(BadRequest($"Controller ID cannot be changed."));
            }

            if (delta.Name != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Name))
                {
                    return(BadRequest($"Invalid controller name: [{delta.Name}]."));
                }
                delta.Name = delta.Name.Trim();
            }

            if (!string.IsNullOrEmpty(delta.m_ControllerTypeText))
            {
                if (!int.TryParse(delta.m_ControllerTypeText.Trim(), out int ctype))
                {
                    if (Enum.TryParse(delta.m_ControllerTypeText.Trim(), true, out KnownControllerTypes ktype))
                    {
                        ctype = (int)ktype;
                    }
                    else
                    {
                        return(BadRequest($"Invalid controller type: [{delta.m_ControllerTypeText}]."));
                    }
                }

                delta.BaseControllerType = ctype;
            }

            if (delta.Version != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Version))
                {
                    return(BadRequest($"Invalid version: [{delta.Version}]."));
                }
                delta.Version = delta.Version.Trim();
            }

            if (delta.Model != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Model))
                {
                    return(BadRequest($"Invalid machine model: [{delta.Model}]."));
                }
                delta.Model = delta.Model.Trim();
            }

            if (delta.IP != null)
            {
                if (string.IsNullOrWhiteSpace(delta.IP))
                {
                    return(BadRequest($"Invalid IP address: [{delta.IP}]."));
                }
                delta.IP = delta.IP.Trim();

                if (!IPorSerialPortRegex.IsMatch(delta.IP))
                {
                    return(BadRequest($"Invalid IP address: [{delta.IP}]."));
                }
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var controller = await db.Controllers
                                 .Where(c => c.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                                 .SingleOrDefaultAsync(c => c.ID == id, ct)
                                 .ConfigureAwait(false);

                if (controller == null)
                {
                    return(NotFound());
                }

                if (delta.m_IsEnabled.HasValue)
                {
                    controller.IsEnabled = delta.IsEnabled.Value;
                }
                if (delta.Name != null)
                {
                    controller.Name = delta.Name;
                }
                if (!string.IsNullOrWhiteSpace(delta.m_ControllerTypeText))
                {
                    controller.Type = delta.BaseControllerType;
                }
                if (delta.Version != null)
                {
                    controller.Version = delta.Version;
                }
                if (delta.Model != null)
                {
                    controller.Model = delta.Model;
                }
                if (delta.IP != null)
                {
                    controller.IP = delta.IP;
                }

                delta.Modified = DateTime.Now;

                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(new ControllerX(controller)));
            }
        }
        public async Task <IActionResult> AddControllerAsync([FromBody] ControllerX controller, CancellationToken ct)
        {
            if (string.IsNullOrWhiteSpace(controller.Name))
            {
                return(BadRequest($"Invalid controller name: [{controller.Name}]."));
            }
            controller.Name = controller.Name.Trim();

            var ctype = 0;

            if (!string.IsNullOrWhiteSpace(controller.m_ControllerTypeText))
            {
                if (!int.TryParse(controller.m_ControllerTypeText.Trim(), out ctype))
                {
                    if (Enum.TryParse(controller.m_ControllerTypeText.Trim(), true, out KnownControllerTypes ktype))
                    {
                        ctype = (int)ktype;
                    }
                    else
                    {
                        return(BadRequest($"Invalid controller type: [{controller.m_ControllerTypeText}]."));
                    }
                }
            }

            controller.BaseControllerType = ctype;

            controller.Version = string.IsNullOrWhiteSpace(controller.Version) ? "0.0.0" : controller.Version.Trim();

            controller.Model = string.IsNullOrWhiteSpace(controller.Model) ? "Unknown" : controller.Model.Trim();

            controller.IP = string.IsNullOrWhiteSpace(controller.IP) ? "1.1.1.1" : controller.IP.Trim();

            if (!IPorSerialPortRegex.IsMatch(controller.IP))
            {
                return(BadRequest($"Invalid IP address: [{controller.IP}]."));
            }

            controller.OrgId    = HttpContext.GetOrg();
            controller.Modified = null;

            using (var db = new ConfigDB()) {
                var cx = await db.Controllers
                         .SingleOrDefaultAsync(c => c.ID == controller.ID, ct)
                         .ConfigureAwait(false);

                if (cx != null)
                {
                    if (cx.OrgId.Equals(controller.OrgId, StringComparison.OrdinalIgnoreCase))
                    {
                        return(BadRequest($"Controller ID [{controller.ID}] already exists."));
                    }
                    return(BadRequest($"Invalid controller ID [{controller.ID}]."));
                }

                cx = controller.GetBase();
                db.Controllers.Add(cx);
                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Created($"controllers/{cx.ID}", new ControllerX(cx)));
            }
        }
        public async Task <IActionResult> UpdateMoldAsync(int id, [FromBody] MoldX delta, CancellationToken ct)
        {
            if (id <= 0)
            {
                return(NotFound());
            }
            if (delta.ID != 0)
            {
                return(BadRequest($"ID cannot be changed."));
            }

            if (delta.Name != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Name))
                {
                    return(BadRequest($"Invalid name: [{delta.Name}]."));
                }
                delta.Name = delta.Name.Trim();
            }

            if (delta.ControllerId.HasValue)
            {
                if (delta.ControllerId < 0)
                {
                    return(BadRequest($"Invalid controller: {delta.ControllerId}."));
                }
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var mold = await db.Molds
                           .Where(m => m.Controller.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                           .SingleOrDefaultAsync(m => m.ID == id, ct)
                           .ConfigureAwait(false);

                if (mold == null)
                {
                    return(NotFound());
                }

                if (delta.m_IsEnabled.HasValue)
                {
                    mold.IsEnabled = delta.IsEnabled.Value;
                }

                if (delta.ControllerId.HasValue)
                {
                    var ctrl = await db.Controllers.AsNoTracking()
                               .Where(c => c.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                               .SingleOrDefaultAsync(c => c.ID == delta.ControllerId, ct)
                               .ConfigureAwait(false);

                    if (ctrl == null)
                    {
                        return(BadRequest($"Invalid controller: {delta.ControllerId}"));
                    }

                    mold.ControllerId = delta.ControllerId;
                }

                if (delta.Name != null)
                {
                    var cx = await db.Molds.AsNoTracking()
                             .Where(m => m.ControllerId == mold.ControllerId)
                             .SingleOrDefaultAsync(m => m.Name.Equals(mold.Name, StringComparison.OrdinalIgnoreCase), ct)
                             .ConfigureAwait(false);

                    if (cx != null)
                    {
                        return(BadRequest($"Controller/Mold [{mold.ControllerId}/{mold.Name}] already exists."));
                    }

                    mold.Name = delta.Name;
                }

                if (delta.Guid != default(Guid))
                {
                    mold.Guid = delta.Guid;
                }
                mold.Modified = DateTime.Now;

                // Replace settings data?
                if (delta.Settings != null)
                {
                    await db.Entry(mold).Collection(m => m.MoldSettings).LoadAsync(ct).ConfigureAwait(false);

                    db.MoldSettings.RemoveRange(mold.MoldSettings.AsEnumerable());

                    var rawsettings = RunLengthEncoder.Decode(delta.Settings);

                    for (var x = 0; x < rawsettings.Count; x++)
                    {
                        // Make sure the last item is always stored to keep the accurate length of the whole data set
                        if (x >= rawsettings.Count - 1 || rawsettings[x] != 0)
                        {
                            db.MoldSettings.Add(new MoldSetting()
                            {
                                MoldId = mold.ID, Offset = (short)x, RawData = rawsettings[x]
                            });
                        }
                    }
                }

                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(new MoldX(mold)));
            }
        }
        public async Task <IActionResult> UpdateUserAsync(int id, [FromBody] UserX delta, CancellationToken ct)
        {
            if (delta.Password != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Password))
                {
                    return(BadRequest("Missing password."));
                }
                delta.Password = delta.Password.Trim();
            }

            if (delta.Name != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Name))
                {
                    return(BadRequest("Missing user name."));
                }
                delta.Name = delta.Name.Trim();
            }

            if (delta.m_AccessLevel.HasValue)
            {
                if (delta.AccessLevel.Value > 10)
                {
                    return(BadRequest($"Invalid access level (0-10): [{delta.AccessLevel}]."));
                }
            }

            if (!string.IsNullOrEmpty(delta.m_FiltersText))
            {
                if (!Enum.TryParse <Filters>(delta.m_FiltersText.Trim(), true, out var filter))
                {
                    return(BadRequest($"Invalid filters: [{delta.m_FiltersText}]."));
                }
                delta.BaseFilters = filter;
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var user = await db.Users
                           .Where(x => x.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                           .SingleOrDefaultAsync(x => x.ID == id, ct)
                           .ConfigureAwait(false);

                if (user == null)
                {
                    return(NotFound());
                }

                if (delta.m_IsEnabled.HasValue)
                {
                    user.IsEnabled = delta.IsEnabled.Value;
                }

                if (delta.Name != null)
                {
                    var cx = await db.Users
                             .Where(x => x.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                             .SingleOrDefaultAsync(x => x.Name.Equals(delta.Name, StringComparison.OrdinalIgnoreCase), ct)
                             .ConfigureAwait(false);

                    if (cx != null)
                    {
                        return(BadRequest($"User name [{delta.Name}] already exists."));
                    }

                    user.Name = delta.Name;
                }

                if (delta.Password != null)
                {
                    user.Password = delta.Password;
                }
                if (!string.IsNullOrWhiteSpace(delta.m_FiltersText))
                {
                    user.Filters = delta.BaseFilters;
                }
                if (delta.m_AccessLevel.HasValue)
                {
                    user.AccessLevel = delta.AccessLevel.Value;
                }

                delta.Modified = DateTime.Now;

                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(new UserX(user)));
            }
        }
        public async Task <IActionResult> AddUserAsync([FromBody] UserX user, CancellationToken ct)
        {
            if (string.IsNullOrWhiteSpace(user.Password))
            {
                return(BadRequest("Missing password."));
            }
            user.Password = user.Password.Trim();

            if (string.IsNullOrWhiteSpace(user.Name))
            {
                return(BadRequest("Missing user name."));
            }
            user.Name = user.Name.Trim();

            if (string.IsNullOrWhiteSpace(user.m_FiltersText))
            {
                return(BadRequest($"Missing filters."));
            }
            if (!Enum.TryParse <Filters>(user.m_FiltersText.Trim(), true, out var filter))
            {
                return(BadRequest($"Invalid filters: [{user.m_FiltersText}]."));
            }
            user.BaseFilters = filter;

            if (user.AccessLevel.Value > 10)
            {
                return(BadRequest($"Invalid access level (0-10): [{user.AccessLevel}]."));
            }

            var orgId = HttpContext.GetOrg();

            user.Modified = null;
            user.OrgId    = orgId;

            using (var db = new ConfigDB()) {
                var ux = await db.Users
                         .Where(x => x.OrgId.Equals(user.OrgId, StringComparison.OrdinalIgnoreCase))
                         .SingleOrDefaultAsync(x => x.Password.Equals(user.Password, StringComparison.OrdinalIgnoreCase), ct)
                         .ConfigureAwait(false);

                if (ux != null)
                {
                    return(BadRequest($"Password [{user.Password}] already exists."));
                }

                ux = await db.Users
                     .Where(x => x.OrgId.Equals(user.OrgId, StringComparison.OrdinalIgnoreCase))
                     .SingleOrDefaultAsync(x => x.Name.Equals(user.Name, StringComparison.OrdinalIgnoreCase), ct)
                     .ConfigureAwait(false);

                if (ux != null)
                {
                    return(BadRequest($"User name [{user.Name}] already exists."));
                }

                ux = user.GetBase();
                db.Users.Add(ux);
                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Created($"users/{ux.ID}", new UserX(ux)));
            }
        }