I think this solution is complete, possibly requires some polishing